diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 32f58b19..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Bug report -description: "Submit Xray-core bug" -body: - - type: checkboxes - attributes: - label: Integrity requirements - description: |- - Please check all of the following options to prove that you have read and understood the requirements, otherwise this issue will be closed. - options: - - label: I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values. - required: true - - label: I provided the complete config and logs, rather than just providing the truncated parts based on my own judgment. - required: true - - label: I searched issues and did not find any similar issues. - required: true - - label: The problem can be successfully reproduced in the latest Release - required: true - - type: textarea - attributes: - label: Description - description: |- - Please provide a detailed description of the error. And the information you think valuable. - If the problem occurs after the update, please provide the **specific** version - validations: - required: true - - type: textarea - attributes: - label: Reproduction Method - description: |- - Based on the configuration you provided below, provide the method to reproduce the bug. - validations: - required: true - - type: markdown - attributes: - value: |- - ## Configuration and Log Section - - ### For config - Please provide the configuration files that can reproduce the problem, including the server and client. - Don't just paste a big exported config file here. Eliminate useless inbound/outbound, rules, options, this can help determine the problem, if you really want to get help. - - ### For logs - Please set the log level to debug and dnsLog to true first. - Restart Xray-core, then operate according to the reproduction method, try to reduce the irrelevant part in the log. - Remember to delete parts with personal information (such as UUID and IP). - Provide the log of Xray-core, not the log output by the panel or other things. - - ### Finally - After removing parts that do not affect reproduction, provide the actual running **complete** file, do not only provide inbound or outbound or a few lines of logs based on your own judgment. - Put the content between the preset ```
``` ```
``` in the text box. - If the problem is very clear that only related to one end (such as core startup failure/crash after correctly writing the config according to the documents), N/A can be filled in for unnecessary areas below. - - type: textarea - attributes: - label: Client config - value: |- -

-
-        
- validations: - required: true - - type: textarea - attributes: - label: Server config - value: |- -

-
-        
- validations: - required: true - - type: textarea - attributes: - label: Client log - value: |- -

-
-        
- validations: - required: true - - type: textarea - attributes: - label: Server log - value: |- -

-
-        
- validations: - required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report_zh.yml b/.github/ISSUE_TEMPLATE/bug_report_zh.yml deleted file mode 100644 index 201f847e..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report_zh.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: bug反馈 -description: "提交 Xray-core bug" -body: - - type: checkboxes - attributes: - label: 完整性要求 - description: |- - 请勾选以下所有选项以证明您已经阅读并理解了以下要求,否则该 issue 将被关闭。 - options: - - label: 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。 - required: true - - label: 我提供了完整的配置文件和日志,而不是出于自己的判断只给出截取的部分。 - required: true - - label: 我搜索了 issues, 没有发现已提出的类似问题。 - required: true - - label: 问题在 Release 最新的版本上可以成功复现 - required: true - - type: textarea - attributes: - label: 描述 - description: |- - 请提供错误的详细描述。以及你认为有价值的信息。 - 如果问题在更新后出现,请提供**具体**出现问题的版本号。 - validations: - required: true - - type: textarea - attributes: - label: 重现方式 - description: |- - 基于你下面提供的配置,提供重现BUG方法。 - validations: - required: true - - type: markdown - attributes: - value: |- - ## 配置与日志部分 - - ### 对于配置文件 - 请提供可以重现问题的配置文件,包括服务端和客户端。 - 不要直接在这里黏贴一大段导出的 config 文件。去掉无用的出入站、规则、选项,这可以帮助确定问题,如果你真的想得到帮助。 - - ### 对于日志 - 请先将日志等级设置为 debug, dnsLog 设置为true. - 重启 Xray-core ,再按复现方式操作,尽量减少日志中的无关部分。 - 记得删除有关个人信息(如UUID与IP)的部分。 - 提供 Xray-core 的日志,而不是面板或者别的东西输出的日志。 - - ### 最后 - 在去掉不影响复现的部分后,提供实际运行的**完整**文件,不要出于自己的判断只提供入站出站或者几行日志。 - 把内容放在文本框预置的 ```
``` 和 ```
``` 中间。 - 如果问题十分明确只出现在某一端(如按文档正确编写配置后核心启动失败/崩溃),可以在下面不需要的项目填入N/A. - - type: textarea - attributes: - label: 客户端配置 - value: |- -

-
-        
- validations: - required: true - - type: textarea - attributes: - label: 服务端配置 - value: |- -

-
-        
- validations: - required: true - - type: textarea - attributes: - label: 客户端日志 - value: |- -

-
-        
- validations: - required: true - - type: textarea - attributes: - label: 服务端日志 - value: |- -

-
-        
- validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 8fe9cb8e..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,4 +0,0 @@ -contact_links: - - name: Community Support and Questions - url: https://github.com/XTLS/Xray-core/discussions - about: Please ask and answer questions there. The issue tracker is for issues with core. diff --git a/.github/build/friendly-filenames.json b/.github/build/friendly-filenames.json index d4001f70..65c20300 100644 --- a/.github/build/friendly-filenames.json +++ b/.github/build/friendly-filenames.json @@ -2,6 +2,7 @@ "android-arm64": { "friendlyName": "android-arm64-v8a" }, "darwin-amd64": { "friendlyName": "macos-64" }, "darwin-arm64": { "friendlyName": "macos-arm64-v8a" }, + "dragonfly-amd64": { "friendlyName": "dragonfly-64" }, "freebsd-386": { "friendlyName": "freebsd-32" }, "freebsd-amd64": { "friendlyName": "freebsd-64" }, "freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" }, @@ -21,7 +22,6 @@ "linux-ppc64le": { "friendlyName": "linux-ppc64le" }, "linux-ppc64": { "friendlyName": "linux-ppc64" }, "linux-riscv64": { "friendlyName": "linux-riscv64" }, - "linux-loong64": { "friendlyName": "linux-loong64" }, "linux-s390x": { "friendlyName": "linux-s390x" }, "openbsd-386": { "friendlyName": "openbsd-32" }, "openbsd-amd64": { "friendlyName": "openbsd-64" }, @@ -31,4 +31,4 @@ "windows-amd64": { "friendlyName": "windows-64" }, "windows-arm64": { "friendlyName": "windows-arm64-v8a" }, "windows-arm7": { "friendlyName": "windows-arm32-v7a" } -} +} \ No newline at end of file diff --git a/.github/docker/Dockerfile b/.github/docker/Dockerfile deleted file mode 100644 index 0b6f2932..00000000 --- a/.github/docker/Dockerfile +++ /dev/null @@ -1,62 +0,0 @@ -# syntax=docker/dockerfile:latest -FROM --platform=$BUILDPLATFORM golang:latest AS build - -# Build xray-core -WORKDIR /src -COPY . . -ARG TARGETOS -ARG TARGETARCH -RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main - -# 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/geosite.dat /tmp/geodat/geosite.dat - -RUN mkdir -p /tmp/empty - -# Create config files with empty JSON content -RUN mkdir -p /tmp/usr/local/etc/xray -RUN cat </tmp/usr/local/etc/xray/00_log.json -{ - "log": { - "error": "/var/log/xray/error.log", - "loglevel": "warning", - "access": "none", - "dnsLog": false - } -} -EOF -RUN echo '{}' >/tmp/usr/local/etc/xray/01_api.json -RUN echo '{}' >/tmp/usr/local/etc/xray/02_dns.json -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/05_inbounds.json -RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json -RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json -RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json -RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json - -# Create log files -RUN mkdir -p /tmp/var/log/xray && touch \ - /tmp/var/log/xray/access.log \ - /tmp/var/log/xray/error.log - -# Build finally image -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 /tmp/empty /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=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=65532:65532 --chmod=600 /tmp/var/log/xray/*.log /var/log/xray/ - -VOLUME /usr/local/etc/xray -VOLUME /var/log/xray - -ARG TZ=Etc/UTC -ENV TZ=$TZ - -ENTRYPOINT [ "/usr/local/bin/xray" ] -CMD [ "-confdir", "/usr/local/etc/xray/" ] diff --git a/.github/docker/Dockerfile.usa b/.github/docker/Dockerfile.usa deleted file mode 100644 index b307bc4b..00000000 --- a/.github/docker/Dockerfile.usa +++ /dev/null @@ -1,71 +0,0 @@ -# syntax=docker/dockerfile:latest -FROM --platform=$BUILDPLATFORM golang:latest AS build - -# Build xray-core -WORKDIR /src -COPY . . -ARG TARGETOS -ARG TARGETARCH -RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main - -# 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/geosite.dat /tmp/geodat/geosite.dat - -RUN mkdir -p /tmp/empty - -# Create config files with empty JSON content -RUN mkdir -p /tmp/usr/local/etc/xray -RUN cat </tmp/usr/local/etc/xray/00_log.json -{ - "log": { - "error": "/var/log/xray/error.log", - "loglevel": "warning", - "access": "none", - "dnsLog": false - } -} -EOF -RUN echo '{}' >/tmp/usr/local/etc/xray/01_api.json -RUN echo '{}' >/tmp/usr/local/etc/xray/02_dns.json -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/05_inbounds.json -RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json -RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json -RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json -RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json - -# Create log files -RUN mkdir -p /tmp/var/log/xray && touch \ - /tmp/var/log/xray/access.log \ - /tmp/var/log/xray/error.log - -# Build finally image -# Note on Distroless Base Image and Architecture Support: -# - The official 'gcr.io/distroless/static' image provided by Google only supports a limited set of architectures for Linux: -# - linux/amd64 -# - linux/arm/v7 -# - linux/arm64/v8 -# - linux/ppc64le -# - linux/s390x -# - Upon inspection, the blob contents of the Distroless images across these architectures are nearly identical, with only minor differences in metadata (e.g., 'Architecture' field in the manifest). -# - Due to this similarity in content, it is feasible to forcibly specify a single platform (e.g., '--platform=linux/amd64') for unsupported architectures, as the core image content remains compatible with statically compiled binaries like Go applications. -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 /tmp/empty /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=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=65532:65532 --chmod=600 /tmp/var/log/xray/*.log /var/log/xray/ - -VOLUME /usr/local/etc/xray -VOLUME /var/log/xray - -ARG TZ=Etc/UTC -ENV TZ=$TZ - -ENTRYPOINT [ "/usr/local/bin/xray" ] -CMD [ "-confdir", "/usr/local/etc/xray/" ] diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index dc7759cc..00000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Build docker image - -on: - release: - types: [published] - -jobs: - build-image: - runs-on: ubuntu-latest - permissions: - packages: write - steps: - - uses: actions/checkout@v4 - - - name: Docker metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository_owner }}/xray-core - flavor: latest=auto - tags: | - type=semver,pattern={{version}} - - - name: Docker metadata (unsupported architectures) - id: metausa - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository_owner }}/xray-core - flavor: | - latest=auto - suffix=-usa,onlatest=true - tags: | - type=semver,pattern={{version}} - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - platforms: | - linux/amd64 - linux/arm/v7 - linux/arm64/v8 - linux/ppc64le - linux/s390x - provenance: false - file: .github/docker/Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - - - name: Build and push (unsupported architectures) - uses: docker/build-push-action@v6 - with: - context: . - platforms: | - linux/386 - linux/arm/v6 - linux/riscv64 - linux/loong64 - provenance: false - file: .github/docker/Dockerfile.usa - push: true - tags: ${{ steps.metausa.outputs.tags }} - - - name: Merge Multi-Arch Manifests - run: | - echo "Starting to merge multi-architecture manifests..." - - # Convert newlines to spaces and split into array - TAGS=($(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' ')) - - echo "Total tags to process: ${#TAGS[@]}" - for tag in "${TAGS[@]}"; do - echo "Merging tag: $tag with unsupported architectures ($tag-usa)" - docker buildx imagetools create --append --tag "$tag" "$tag-usa" - if [ $? -ne 0 ]; then - echo "Error: Failed to merge $tag-usa into $tag" - exit 1 - fi - done - - echo "Multi-architecture manifest merge completed successfully." diff --git a/.github/workflows/release-win7.yml b/.github/workflows/release-win7.yml deleted file mode 100644 index be9173e3..00000000 --- a/.github/workflows/release-win7.yml +++ /dev/null @@ -1,148 +0,0 @@ -name: Build and Release for Windows 7 - -on: - workflow_dispatch: - release: - types: [published] - push: - pull_request: - types: [opened, synchronize, reopened] - -jobs: - check-assets: - runs-on: ubuntu-latest - steps: - - name: Restore Geodat Cache - uses: actions/cache/restore@v4 - with: - path: resources - key: xray-geodat- - - - name: Check Assets Existence - id: check-assets - run: | - [ -d 'resources' ] || mkdir resources - LIST=('geoip.dat' 'geosite.dat') - for FILE_NAME in "${LIST[@]}" - do - echo -e "Checking ${FILE_NAME}..." - if [ -s "./resources/${FILE_NAME}" ]; then - echo -e "${FILE_NAME} exists." - else - echo -e "${FILE_NAME} does not exist." - echo "missing=true" >> $GITHUB_OUTPUT - break - fi - done - - - name: Sleep for 90 seconds if Assets Missing - if: steps.check-assets.outputs.missing == 'true' - run: sleep 90 - - build: - needs: check-assets - permissions: - contents: write - strategy: - matrix: - include: - # BEGIN Windows 7 - - goos: windows - goarch: amd64 - assetname: win7-64 - - goos: windows - goarch: 386 - assetname: win7-32 - # END Windows 7 - fail-fast: false - - runs-on: ubuntu-latest - env: - GOOS: ${{ matrix.goos}} - GOARCH: ${{ matrix.goarch }} - CGO_ENABLED: 0 - steps: - - name: Checkout codebase - uses: actions/checkout@v4 - - - name: Show workflow information - run: | - _NAME=${{ matrix.assetname }} - echo "GOOS: ${{ matrix.goos }}, GOARCH: ${{ matrix.goarch }}, RELEASE_NAME: $_NAME" - echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - check-latest: true - - - name: Setup patched builder - run: | - GOSDK=$(go env GOROOT) - rm -r $GOSDK/* - cd $GOSDK - curl -O -L -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://github.com/XTLS/go-win7/releases/latest/download/go-for-win7-linux-amd64.zip - unzip ./go-for-win7-linux-amd64.zip -d $GOSDK - rm ./go-for-win7-linux-amd64.zip - - - name: Get project dependencies - run: go mod download - - - name: Build Xray - run: | - mkdir -p build_assets - COMMID=$(git describe --always --dirty) - echo 'Building Xray for Windows 7...' - go build -o build_assets/xray.exe -trimpath -buildvcs=false -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. - # go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main - - - name: Restore Geodat Cache - uses: actions/cache/restore@v4 - with: - path: resources - key: xray-geodat- - - - name: Copy README.md & LICENSE - run: | - mv -f resources/* build_assets - cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md - cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE - - - name: Create ZIP archive - if: github.event_name == 'release' - shell: bash - run: | - pushd build_assets || exit 1 - touch -mt $(date +%Y01010000) * - zip -9vr ../Xray-${{ env.ASSET_NAME }}.zip . - popd || exit 1 - FILE=./Xray-${{ env.ASSET_NAME }}.zip - DGST=$FILE.dgst - for METHOD in {"md5","sha1","sha256","sha512"} - do - openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST - 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 - uses: svenstaro/upload-release-action@v2 - if: github.event_name == 'release' - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ./Xray-${{ env.ASSET_NAME }}.zip* - tag: ${{ github.ref }} - file_glob: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b18e94ad..edbc01c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,71 +5,81 @@ on: release: types: [published] push: + branches: + - main + paths: + - "**/*.go" + - "go.mod" + - "go.sum" + - ".github/workflows/*.yml" pull_request: types: [opened, synchronize, reopened] - + paths: + - "**/*.go" + - "go.mod" + - "go.sum" + - ".github/workflows/*.yml" jobs: - check-assets: + prepare: runs-on: ubuntu-latest steps: - - name: Restore Geodat Cache - uses: actions/cache/restore@v4 + - name: Restore Cache + uses: actions/cache/restore@v3 with: path: resources key: xray-geodat- - - name: Check Assets Existence - id: check-assets - run: | - [ -d 'resources' ] || mkdir resources - LIST=('geoip.dat' 'geosite.dat') - for FILE_NAME in "${LIST[@]}" - do - echo -e "Checking ${FILE_NAME}..." - if [ -s "./resources/${FILE_NAME}" ]; then - echo -e "${FILE_NAME} exists." - else - echo -e "${FILE_NAME} does not exist." - echo "missing=true" >> $GITHUB_OUTPUT - break - fi - done - - - name: Trigger Asset Update Workflow if Assets Missing - if: steps.check-assets.outputs.missing == 'true' - uses: actions/github-script@v7 + - name: Update Geodat + id: update + uses: nick-fields/retry@v2 with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { owner, repo } = context.repo; - await github.rest.actions.createWorkflowDispatch({ - owner, - repo, - workflow_id: 'scheduled-assets-update.yml', - ref: context.ref - }); - console.log('Triggered scheduled-assets-update.yml due to missing assets on branch:', context.ref); + timeout_minutes: 60 + retry_wait_seconds: 60 + max_attempts: 60 + command: | + [ -d 'resources' ] || mkdir resources + LIST=('geoip geoip geoip' 'domain-list-community dlc geosite') + for i in "${LIST[@]}" + do + INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}')) + FILE_NAME="${INFO[2]}.dat" + echo -e "Verifying HASH key..." + HASH="$(curl -sL "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')" + if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then + continue + else + echo -e "Downloading https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat..." + curl -L "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat" -o ./resources/${FILE_NAME} + echo -e "Verifying HASH key..." + [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; } + echo "unhit=true" >> $GITHUB_OUTPUT + fi + done - - name: Sleep for 90 seconds if Assets Missing - if: steps.check-assets.outputs.missing == 'true' - run: sleep 90 + - name: Save Cache + uses: actions/cache/save@v3 + if: ${{ steps.update.outputs.unhit }} + with: + path: resources + key: xray-geodat-${{ github.sha }}-${{ github.run_number }} build: - needs: check-assets + needs: prepare permissions: contents: write strategy: matrix: # Include amd64 on all platforms. - goos: [windows, freebsd, openbsd, linux, darwin] + goos: [windows, freebsd, openbsd, linux, dragonfly, darwin] goarch: [amd64, 386] - patch-assetname: [""] exclude: - # Exclude i386 on darwin + # Exclude i386 on darwin and dragonfly. + - goarch: 386 + goos: dragonfly - goarch: 386 goos: darwin include: - # BEGIN MacOS ARM64 + # BEIGIN MacOS ARM64 - goos: darwin goarch: arm64 # END MacOS ARM64 @@ -88,11 +98,6 @@ jobs: - goos: android goarch: arm64 # END Android ARM 8 - # BEGIN Android AMD64 - - goos: android - goarch: amd64 - patch-assetname: android-amd64 - # END Android AMD64 # Windows ARM - goos: windows goarch: arm64 @@ -100,14 +105,12 @@ jobs: goarch: arm goarm: 7 # BEGIN Other architectures - # BEGIN riscv64 & ARM64 & LOONG64 + # BEGIN riscv64 & ARM64 - goos: linux goarch: arm64 - goos: linux goarch: riscv64 - - goos: linux - goarch: loong64 - # END riscv64 & ARM64 & LOONG64 + # END riscv64 & ARM64 # BEGIN MIPS - goos: linux goarch: mips64 @@ -153,59 +156,56 @@ jobs: CGO_ENABLED: 0 steps: - name: Checkout codebase - uses: actions/checkout@v4 + uses: actions/checkout@v3 - - name: Set up NDK - if: matrix.goos == 'android' + - name: Show workflow information run: | - wget -qO android-ndk.zip https://dl.google.com/android/repository/android-ndk-r28b-linux.zip - unzip android-ndk.zip - rm android-ndk.zip - declare -A arches=( - ["amd64"]="x86_64-linux-android24-clang" - ["arm64"]="aarch64-linux-android24-clang" - ) - echo CC="$(realpath android-ndk-*/toolchains/llvm/prebuilt/linux-x86_64/bin)/${arches[${{ matrix.goarch }}]}" >> $GITHUB_ENV - echo CGO_ENABLED=1 >> $GITHUB_ENV - - - name: Show workflow information - run: | - _NAME=${{ matrix.patch-assetname }} - [ -n "$_NAME" ] || _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json) + export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json) echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME" echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v3 with: - go-version-file: go.mod + go-version: '1.20' check-latest: true - name: Get project dependencies run: go mod download - + + - name: Replace Custom to Commit ID + if: github.event_name != 'release' + run: | + ID=$(git rev-parse --short ${{ github.sha }}) + if [ "${{ github.event_name }}" == 'pull_request' ] + then + ID=$(git rev-parse --short ${{ github.event.pull_request.head.sha }}) + fi + sed -i '/build/ s/Custom/'$ID'/' ./core/core.go + - name: Build Xray run: | mkdir -p build_assets - COMMID=$(git describe --always --dirty) - if [[ ${GOOS} == 'windows' ]]; then - echo 'Building Xray for Windows...' - go build -o build_assets/xray.exe -trimpath -buildvcs=false -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. - # go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main - else - echo 'Building Xray...' - go build -o build_assets/xray -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main - if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then - echo 'Building soft-float Xray for MIPS/MIPSLE 32-bit...' - GOMIPS=softfloat go build -o build_assets/xray_softfloat -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main - fi - fi + go build -v -o build_assets/xray -trimpath -ldflags "-s -w -buildid=" ./main - - name: Restore Geodat Cache - uses: actions/cache/restore@v4 + - name: Build background Xray on Windows + if: matrix.goos == 'windows' + run: | + go build -v -o build_assets/wxray.exe -trimpath -ldflags "-s -w -H windowsgui -buildid=" ./main + + - name: Build Mips softfloat Xray + if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle' + run: | + GOMIPS=softfloat go build -v -o build_assets/xray_softfloat -trimpath -ldflags "-s -w -buildid=" ./main + + - name: Rename Windows Xray + if: matrix.goos == 'windows' + run: | + cd ./build_assets || exit 1 + mv xray xray.exe + + - name: Restore Cache + uses: actions/cache/restore@v3 with: path: resources key: xray-geodat- @@ -217,7 +217,6 @@ jobs: cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE - name: Create ZIP archive - if: github.event_name == 'release' shell: bash run: | pushd build_assets || exit 1 @@ -236,7 +235,7 @@ jobs: mv build_assets Xray-${{ env.ASSET_NAME }} - name: Upload files to Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: Xray-${{ env.ASSET_NAME }} path: | diff --git a/.github/workflows/scheduled-assets-update.yml b/.github/workflows/scheduled-assets-update.yml deleted file mode 100644 index 089c3b33..00000000 --- a/.github/workflows/scheduled-assets-update.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Scheduled assets update - -# NOTE: This Github Actions is required by other actions, for preparing other packaging assets in a -# routine manner, for example: GeoIP/GeoSite. -# Currently updating: -# - Geodat (GeoIP/Geosite) - -on: - workflow_dispatch: - schedule: - # Update GeoData on every day (22:30 UTC) - - cron: "30 22 * * *" - push: - # Prevent triggering update request storm - paths: - - ".github/workflows/scheduled-assets-update.yml" - pull_request: - # Prevent triggering update request storm - paths: - - ".github/workflows/scheduled-assets-update.yml" - -jobs: - geodat: - if: github.event.schedule == '30 22 * * *' || github.event_name == 'push'|| github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - steps: - - name: Restore Geodat Cache - uses: actions/cache/restore@v4 - with: - path: resources - key: xray-geodat- - - - name: Update Geodat - id: update - uses: nick-fields/retry@v3 - with: - timeout_minutes: 60 - retry_wait_seconds: 60 - max_attempts: 60 - command: | - [ -d 'resources' ] || mkdir resources - LIST=('Loyalsoldier v2ray-rules-dat geoip geoip' 'Loyalsoldier v2ray-rules-dat geosite geosite') - for i in "${LIST[@]}" - do - INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3,$4}')) - FILE_NAME="${INFO[3]}.dat" - echo -e "Verifying HASH key..." - HASH="$(curl -sL -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat.sha256sum" | awk -F ' ' '{print $1}')" - if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then - continue - else - echo -e "Downloading https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat..." - curl -L -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat" -o ./resources/${FILE_NAME} - echo -e "Verifying HASH key..." - [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; } - echo "unhit=true" >> $GITHUB_OUTPUT - fi - done - - - name: Save Geodat Cache - uses: actions/cache/save@v4 - if: ${{ steps.update.outputs.unhit }} - with: - path: resources - key: xray-geodat-${{ github.sha }}-${{ github.run_number }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c853a143..49af621b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,40 +2,23 @@ name: Test on: push: + branches: + - main + paths: + - "**/*.go" + - "go.mod" + - "go.sum" + - ".github/workflows/*.yml" pull_request: types: [opened, synchronize, reopened] + paths: + - "**/*.go" + - "go.mod" + - "go.sum" + - ".github/workflows/*.yml" jobs: - check-assets: - runs-on: ubuntu-latest - steps: - - name: Restore Geodat Cache - uses: actions/cache/restore@v4 - with: - path: resources - key: xray-geodat- - - name: Check Assets Existence - id: check-assets - run: | - [ -d 'resources' ] || mkdir resources - LIST=('geoip.dat' 'geosite.dat') - for FILE_NAME in "${LIST[@]}" - do - echo -e "Checking ${FILE_NAME}..." - if [ -s "./resources/${FILE_NAME}" ]; then - echo -e "${FILE_NAME} exists." - else - echo -e "${FILE_NAME} does not exist." - echo "missing=true" >> $GITHUB_OUTPUT - break - fi - done - - name: Sleep for 90 seconds if Assets Missing - if: steps.check-assets.outputs.missing == 'true' - run: sleep 90 - test: - needs: check-assets permissions: contents: read runs-on: ${{ matrix.os }} @@ -44,15 +27,15 @@ jobs: matrix: os: [windows-latest, ubuntu-latest, macos-latest] steps: - - name: Checkout codebase - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v3 with: - go-version-file: go.mod + go-version: '1.20' check-latest: true - - name: Restore Geodat Cache - uses: actions/cache/restore@v4 + - name: Checkout codebase + uses: actions/checkout@v3 + - name: Restore Cache + uses: actions/cache/restore@v3 with: path: resources key: xray-geodat- diff --git a/.gitignore b/.gitignore index ac744f79..9242f587 100644 --- a/.gitignore +++ b/.gitignore @@ -14,33 +14,15 @@ # Dependency directories (remove the comment below to include it) # vendor/ -# macOS specific files *.DS_Store - -# IDE specific files -.idea/ -.vscode/ - -# Archive files +.idea *.zip *.tar.gz - -# Binaries xray -xray_softfloat mockgen vprotogen !infra/vprotogen/ errorgen !common/errors/errorgen/ *.dat - -# Build assets -/build_assets - -# Output from dlv test -**/debug.* - -# Certificates -*.crt -*.key +.vscode diff --git a/README.md b/README.md index ffa4f547..7579cb91 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,92 @@ # Project X -[Project X](https://github.com/XTLS) originates from XTLS protocol, providing a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core) and [REALITY](https://github.com/XTLS/REALITY). - -[README](https://github.com/XTLS/Xray-core#readme) is open, so feel free to submit your project [here](https://github.com/XTLS/Xray-core/pulls). - -## Donation & NFTs - -- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`** -- **Project X NFT: [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633)** -- **REALITY NFT: [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)** +[Project X](https://github.com/XTLS) originates from XTLS protocol, provides a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core). ## License [Mozilla Public License Version 2.0](https://github.com/XTLS/Xray-core/blob/main/LICENSE) -## Documentation +## Installation -[Project X Official Website](https://xtls.github.io) +- Linux Script + - [Xray-install](https://github.com/XTLS/Xray-install) + - [Xray-script](https://github.com/kirin10000/Xray-script) +- Docker + - [teddysun/xray](https://hub.docker.com/r/teddysun/xray) +- One Click + - [ProxySU](https://github.com/proxysu/ProxySU) + - [v2ray-agent](https://github.com/mack-a/v2ray-agent) + - [Xray-yes](https://github.com/jiuqi9997/Xray-yes) + - [Xray_onekey](https://github.com/wulabing/Xray_onekey) +- Magisk + - [Xray4Magisk](https://github.com/CerteKim/Xray4Magisk) + - [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk) +- Homebrew + - `brew install xray` + - [(Tap) Repository 0](https://github.com/N4FA/homebrew-xray) + - [(Tap) Repository 1](https://github.com/xiruizhao/homebrew-xray) + +## Contributing +[Code Of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md) + +## Usage + +[Xray-examples](https://github.com/XTLS/Xray-examples) / [VLESS-TCP-XTLS-WHATEVER](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-WHATEVER) + +## GUI Clients + +- OpenWrt + - [PassWall](https://github.com/xiaorouji/openwrt-passwall) + - [Hello World](https://github.com/jerrykuku/luci-app-vssr) + - [ShadowSocksR Plus+](https://github.com/fw876/helloworld) + - [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray)) +- Windows + - [v2rayN](https://github.com/2dust/v2rayN) + - [Qv2ray](https://github.com/Qv2ray/Qv2ray) (This project had been archived and currently inactive) + - [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch) (This project had been archived and currently inactive) +- Android + - [v2rayNG](https://github.com/2dust/v2rayNG) + - [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls) +- iOS & macOS (with M1 chip) + - [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118) + - [Stash](https://apps.apple.com/app/stash/id1596063349) +- macOS (Intel chip & M1 chip) + - [Qv2ray](https://github.com/Qv2ray/Qv2ray) (This project had been archived and currently inactive) + - [V2RayXS](https://github.com/tzmax/V2RayXS) + +## Credits + +This repo relies on the following third-party projects: + +- Special thanks: + - [v2fly/v2ray-core](https://github.com/v2fly/v2ray-core) +- In production: + - [ghodss/yaml](https://github.com/ghodss/yaml) + - [gorilla/websocket](https://github.com/gorilla/websocket) + - [quic-go/quic-go](https://github.com/quic-go/quic-go) + - [pelletier/go-toml](https://github.com/pelletier/go-toml) + - [pires/go-proxyproto](https://github.com/pires/go-proxyproto) + - [refraction-networking/utls](https://github.com/refraction-networking/utls) + - [seiflotfy/cuckoofilter](https://github.com/seiflotfy/cuckoofilter) + - [google/starlark-go](https://github.com/google/starlark-go) +- For testing only: + - [miekg/dns](https://github.com/miekg/dns) + - [stretchr/testify](https://github.com/stretchr/testify) + - [h12w/socks](https://github.com/h12w/socks) + +## Compilation + +### Windows + +```bash +go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main +``` + +### Linux / macOS + +```bash +go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main +``` ## Telegram @@ -24,139 +94,6 @@ [Project X Channel](https://t.me/projectXtls) -[Project VLESS](https://t.me/projectVless) (Русский) - -[Project XHTTP](https://t.me/projectXhttp) (Persian) - -## Installation - -- Linux Script - - [XTLS/Xray-install](https://github.com/XTLS/Xray-install) (**Official**) - - [tempest](https://github.com/team-cloudchaser/tempest) (supports [`systemd`](https://systemd.io) and [OpenRC](https://github.com/OpenRC/openrc); Linux-only) -- Docker - - [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**) - - [teddysun/xray](https://hub.docker.com/r/teddysun/xray) - - [wulabing/xray_docker](https://github.com/wulabing/xray_docker) -- 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) - - [Marzban](https://github.com/Gozargah/Marzban) - - [Xray-UI](https://github.com/qist/xray-ui) - - [Hiddify](https://github.com/hiddify/Hiddify-Manager) -- 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_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) -- Magisk - - [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk) - - [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk) -- Homebrew - - `brew install xray` - -## Usage - -- Example - - [VLESS-XTLS-uTLS-REALITY](https://github.com/XTLS/REALITY#readme) - - [VLESS-TCP-XTLS-Vision](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-Vision) - - [All-in-One-fallbacks-Nginx](https://github.com/XTLS/Xray-examples/tree/main/All-in-One-fallbacks-Nginx) -- Xray-examples - - [XTLS/Xray-examples](https://github.com/XTLS/Xray-examples) - - [chika0801/Xray-examples](https://github.com/chika0801/Xray-examples) - - [lxhao61/integrated-examples](https://github.com/lxhao61/integrated-examples) -- Tutorial - - [XTLS Vision](https://github.com/chika0801/Xray-install) - - [REALITY (English)](https://cscot.pages.dev/2023/03/02/Xray-REALITY-tutorial/) - - [XTLS-Iran-Reality (English)](https://github.com/SasukeFreestyle/XTLS-Iran-Reality) - - [Xray REALITY with 'steal oneself' (English)](https://computerscot.github.io/vless-xtls-utls-reality-steal-oneself.html) - - [Xray with WireGuard inbound (English)](https://g800.pages.dev/wireguard) - -## GUI Clients - -- OpenWrt - - [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2) - - [ShadowSocksR Plus+](https://github.com/fw876/helloworld) - - [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray)) -- Asuswrt-Merlin - - [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui) -- Windows - - [v2rayN](https://github.com/2dust/v2rayN) - - [Furious](https://github.com/LorenEteval/Furious) - - [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient) -- Android - - [v2rayNG](https://github.com/2dust/v2rayNG) - - [X-flutter](https://github.com/XTLS/X-flutter) - - [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray) - - [SimpleXray](https://github.com/lhear/SimpleXray) -- iOS & macOS arm64 - - [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) - - [Streisand](https://apps.apple.com/app/streisand/id6450534064) - - [OneXray](https://github.com/OneXray/OneXray) -- macOS arm64 & x64 - - [V2rayU](https://github.com/yanue/V2rayU) - - [V2RayXS](https://github.com/tzmax/V2RayXS) - - [Furious](https://github.com/LorenEteval/Furious) - - [OneXray](https://github.com/OneXray/OneXray) -- Linux - - [v2rayA](https://github.com/v2rayA/v2rayA) - - [Furious](https://github.com/LorenEteval/Furious) - - [GorzRay](https://github.com/ketetefid/GorzRay) - -## Others that support VLESS, XTLS, REALITY, XUDP, PLUX... - -- iOS & macOS arm64 - - [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118) - - [Loon](https://apps.apple.com/us/app/loon/id1373567447) -- Xray Tools - - [xray-knife](https://github.com/lilendian0x00/xray-knife) - - [xray-checker](https://github.com/kutovoys/xray-checker) -- Xray Wrapper - - [XTLS/libXray](https://github.com/XTLS/libXray) - - [xtls-sdk](https://github.com/remnawave/xtls-sdk) - - [xtlsapi](https://github.com/hiddify/xtlsapi) - - [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite) - - [Xray-core-python](https://github.com/LorenEteval/Xray-core-python) - - [xray-api](https://github.com/XVGuardian/xray-api) -- [XrayR](https://github.com/XrayR-project/XrayR) - - [XrayR-release](https://github.com/XrayR-project/XrayR-release) - - [XrayR-V2Board](https://github.com/missuo/XrayR-V2Board) -- Cores - - [Amnezia VPN](https://github.com/amnezia-vpn) - - [mihomo](https://github.com/MetaCubeX/mihomo) - - [sing-box](https://github.com/SagerNet/sing-box) - -## Contributing - -[Code of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md) - -[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/XTLS/Xray-core) - -## Credits - -- [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). - -## One-line Compilation - -### Windows (PowerShell) - -```powershell -$env:CGO_ENABLED=0 -go build -o xray.exe -trimpath -buildvcs=false -ldflags="-s -w -buildid=" -v ./main -``` - -### Linux / macOS - -```bash -CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-s -w -buildid=" -v ./main -``` - -### Reproducible Releases - -Make sure that you are using the same Go version, and remember to set the git commit id (7 bytes): - -```bash -CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main -``` - ## Stargazers over time [![Stargazers over time](https://starchart.cc/XTLS/Xray-core.svg)](https://starchart.cc/XTLS/Xray-core) diff --git a/app/commander/commander.go b/app/commander/commander.go index 9ea71e6e..c03da83f 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -1,12 +1,13 @@ package commander +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "net" "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/signal/done" core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/outbound" @@ -20,14 +21,12 @@ type Commander struct { services []Service ohm outbound.Manager tag string - listen string } // NewCommander creates a new Commander based on the given config. func NewCommander(ctx context.Context, config *Config) (*Commander, error) { c := &Commander{ - tag: config.Tag, - listen: config.Listen, + tag: config.Tag, } common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) { @@ -45,7 +44,7 @@ func NewCommander(ctx context.Context, config *Config) (*Commander, error) { } service, ok := rawService.(Service) if !ok { - return nil, errors.New("not a Service.") + return nil, newError("not a Service.") } c.services = append(c.services, service) } @@ -67,32 +66,19 @@ func (c *Commander) Start() error { } c.Unlock() - var listen = func(listener net.Listener) { - if err := c.server.Serve(listener); err != nil { - errors.LogErrorInner(context.Background(), err, "failed to start grpc server") - } - } - - if len(c.listen) > 0 { - if l, err := net.Listen("tcp", c.listen); err != nil { - errors.LogErrorInner(context.Background(), err, "API server failed to listen on ", c.listen) - return err - } else { - errors.LogInfo(context.Background(), "API server listening on ", l.Addr()) - go listen(l) - } - return nil - } - listener := &OutboundListener{ buffer: make(chan net.Conn, 4), done: done.New(), } - go listen(listener) + go func() { + if err := c.server.Serve(listener); err != nil { + newError("failed to start grpc server").Base(err).AtError().WriteToLog() + } + }() if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil { - errors.LogInfoInner(context.Background(), err, "failed to remove existing handler") + newError("failed to remove existing handler").WriteToLog() } return c.ohm.AddHandler(context.Background(), &Outbound{ diff --git a/app/commander/config.pb.go b/app/commander/config.pb.go index 94c40413..1ebbe094 100644 --- a/app/commander/config.pb.go +++ b/app/commander/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/commander/config.proto package commander @@ -29,8 +29,6 @@ type Config struct { // Tag of the outbound handler that handles grpc connections. Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` - // Network address of commander grpc service. - Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"` // Services that supported by this server. All services must implement Service // interface. Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"` @@ -38,9 +36,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_commander_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_commander_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -51,7 +51,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_commander_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -73,13 +73,6 @@ func (x *Config) GetTag() string { return "" } -func (x *Config) GetListen() string { - if x != nil { - return x.Listen - } - return "" -} - func (x *Config) GetService() []*serial.TypedMessage { if x != nil { return x.Service @@ -96,9 +89,11 @@ type ReflectionConfig struct { func (x *ReflectionConfig) Reset() { *x = ReflectionConfig{} - mi := &file_app_commander_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_commander_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ReflectionConfig) String() string { @@ -109,7 +104,7 @@ func (*ReflectionConfig) ProtoMessage() {} func (x *ReflectionConfig) ProtoReflect() protoreflect.Message { mi := &file_app_commander_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -132,21 +127,20 @@ var file_app_commander_config_proto_rawDesc = []byte{ 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, + 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, - 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, + 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 ( @@ -162,7 +156,7 @@ func file_app_commander_config_proto_rawDescGZIP() []byte { } var file_app_commander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_app_commander_config_proto_goTypes = []any{ +var file_app_commander_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.app.commander.Config (*ReflectionConfig)(nil), // 1: xray.app.commander.ReflectionConfig (*serial.TypedMessage)(nil), // 2: xray.common.serial.TypedMessage @@ -181,6 +175,32 @@ func file_app_commander_config_proto_init() { if File_app_commander_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_commander_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_commander_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReflectionConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/commander/config.proto b/app/commander/config.proto index 688949d9..91d7b061 100644 --- a/app/commander/config.proto +++ b/app/commander/config.proto @@ -12,10 +12,6 @@ import "common/serial/typed_message.proto"; message Config { // Tag of the outbound handler that handles grpc connections. string tag = 1; - - // Network address of commander grpc service. - string listen = 3; - // Services that supported by this server. All services must implement Service // interface. repeated xray.common.serial.TypedMessage service = 2; diff --git a/app/commander/errors.generated.go b/app/commander/errors.generated.go new file mode 100644 index 00000000..c3cac329 --- /dev/null +++ b/app/commander/errors.generated.go @@ -0,0 +1,9 @@ +package commander + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/commander/outbound.go b/app/commander/outbound.go index 7f520d74..aa60bd1c 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -5,10 +5,8 @@ import ( "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" - "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/transport" ) @@ -33,7 +31,7 @@ func (l *OutboundListener) add(conn net.Conn) { func (l *OutboundListener) Accept() (net.Conn, error) { select { case <-l.done.Wait(): - return nil, errors.New("listen closed") + return nil, newError("listen closed") case c := <-l.buffer: return c, nil } @@ -109,13 +107,3 @@ func (co *Outbound) Close() error { co.closed = true return co.listener.Close() } - -// SenderSettings implements outbound.Handler. -func (co *Outbound) SenderSettings() *serial.TypedMessage { - return nil -} - -// ProxySettings implements outbound.Handler. -func (co *Outbound) ProxySettings() *serial.TypedMessage { - return nil -} diff --git a/app/dispatcher/config.pb.go b/app/dispatcher/config.pb.go index a196e2e1..04e8d7a3 100644 --- a/app/dispatcher/config.pb.go +++ b/app/dispatcher/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/dispatcher/config.proto package dispatcher @@ -28,9 +28,11 @@ type SessionConfig struct { func (x *SessionConfig) Reset() { *x = SessionConfig{} - mi := &file_app_dispatcher_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_dispatcher_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SessionConfig) String() string { @@ -41,7 +43,7 @@ func (*SessionConfig) ProtoMessage() {} func (x *SessionConfig) ProtoReflect() protoreflect.Message { mi := &file_app_dispatcher_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -66,9 +68,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_dispatcher_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_dispatcher_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -79,7 +83,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_dispatcher_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -135,7 +139,7 @@ func file_app_dispatcher_config_proto_rawDescGZIP() []byte { } var file_app_dispatcher_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_app_dispatcher_config_proto_goTypes = []any{ +var file_app_dispatcher_config_proto_goTypes = []interface{}{ (*SessionConfig)(nil), // 0: xray.app.dispatcher.SessionConfig (*Config)(nil), // 1: xray.app.dispatcher.Config } @@ -153,6 +157,32 @@ func file_app_dispatcher_config_proto_init() { if File_app_dispatcher_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_dispatcher_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SessionConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_dispatcher_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 544a0956..7328d213 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -1,15 +1,16 @@ package dispatcher +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - "regexp" + "fmt" "strings" "sync" "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" @@ -25,7 +26,7 @@ import ( "github.com/xtls/xray-core/transport/pipe" ) -var errSniffingTimeout = errors.New("timeout on sniffing") +var errSniffingTimeout = newError("timeout on sniffing") type cachedReader struct { sync.Mutex @@ -33,21 +34,17 @@ type cachedReader struct { cache buf.MultiBuffer } -func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error { - mb, err := r.reader.ReadMultiBufferTimeout(deadline) - if err != nil { - return err - } +func (r *cachedReader) Cache(b *buf.Buffer) { + mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100) r.Lock() if !mb.IsEmpty() { r.cache, _ = buf.MergeMulti(r.cache, mb) } b.Clear() - rawBytes := b.Extend(min(r.cache.Len(), b.Cap())) + rawBytes := b.Extend(buf.Size) n := r.cache.Copy(rawBytes) b.Resize(0, int32(n)) r.Unlock() - return nil } func (r *cachedReader) readInternal() buf.MultiBuffer { @@ -96,6 +93,7 @@ type DefaultDispatcher struct { router routing.Router policy policy.Manager stats stats.Manager + dns dns.Client fdns dns.FakeDNSEngine } @@ -103,10 +101,10 @@ func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { d := new(DefaultDispatcher) if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error { - core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) { + core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { d.fdns = fdns }) - return d.Init(config.(*Config), om, router, pm, sm) + return d.Init(config.(*Config), om, router, pm, sm, dc) }); err != nil { return nil, err } @@ -115,11 +113,12 @@ func init() { } // Init initializes DefaultDispatcher. -func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error { +func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error { d.ohm = om d.router = router d.policy = pm d.stats = sm + d.dns = dns return nil } @@ -136,10 +135,77 @@ func (*DefaultDispatcher) Start() error { // Close implements common.Closable. func (*DefaultDispatcher) Close() error { return nil } -func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *transport.Link) { - opt := pipe.OptionsFromContext(ctx) - uplinkReader, uplinkWriter := pipe.New(opt...) - downlinkReader, downlinkWriter := pipe.New(opt...) +func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link) { + downOpt := pipe.OptionsFromContext(ctx) + upOpt := downOpt + + if network == net.Network_UDP { + var ip2domain *sync.Map // net.IP.String() => domain, this map is used by server side when client turn on fakedns + // Client will send domain address in the buffer.UDP.Address, server record all possible target IP addrs. + // When target replies, server will restore the domain and send back to client. + // Note: this map is not global but per connection context + upOpt = append(upOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer { + for i, buffer := range mb { + if buffer.UDP == nil { + continue + } + addr := buffer.UDP.Address + if addr.Family().IsIP() { + if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(addr) && sniffing.Enabled { + domain := fkr0.GetDomainFromFakeDNS(addr) + if len(domain) > 0 { + buffer.UDP.Address = net.DomainAddress(domain) + newError("[fakedns client] override with domain: ", domain, " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx)) + } else { + newError("[fakedns client] failed to find domain! :", addr.String(), " for xUDP buffer at ", i).AtWarning().WriteToLog(session.ExportIDToError(ctx)) + } + } + } else { + if ip2domain == nil { + ip2domain = new(sync.Map) + newError("[fakedns client] create a new map").WriteToLog(session.ExportIDToError(ctx)) + } + domain := addr.Domain() + ips, err := d.dns.LookupIP(domain, dns.IPOption{true, true, false}) + if err == nil { + for _, ip := range ips { + ip2domain.Store(ip.String(), domain) + } + newError("[fakedns client] candidate ip: "+fmt.Sprintf("%v", ips), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx)) + } else { + newError("[fakedns client] failed to look up IP for ", domain, " for xUDP buffer at ", i).Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + } + } + return mb + })) + downOpt = append(downOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer { + for i, buffer := range mb { + if buffer.UDP == nil { + continue + } + addr := buffer.UDP.Address + if addr.Family().IsIP() { + if ip2domain == nil { + continue + } + if domain, found := ip2domain.Load(addr.IP().String()); found { + buffer.UDP.Address = net.DomainAddress(domain.(string)) + newError("[fakedns client] restore domain: ", domain.(string), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx)) + } + } else { + if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok { + fakeIp := fkr0.GetFakeIPForDomain(addr.Domain()) + buffer.UDP.Address = fakeIp[0] + newError("[fakedns client] restore FakeIP: ", buffer.UDP, fmt.Sprintf("%v", fakeIp), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx)) + } + } + } + return mb + })) + } + uplinkReader, uplinkWriter := pipe.New(upOpt...) + downlinkReader, downlinkWriter := pipe.New(downOpt...) inboundLink := &transport.Link{ Reader: downlinkReader, @@ -177,18 +243,6 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran } } } - - if p.Stats.UserOnline { - name := "user>>>" + user.Email + ">>>online" - if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil { - sessionInbounds := session.InboundFromContext(ctx) - userIP := sessionInbounds.Source.Address.String() - om.AddIP(userIP) - // log Online user with ips - // errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List()) - - } - } } return inboundLink, outboundLink @@ -200,20 +254,8 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu return false } for _, d := range request.ExcludeForDomain { - if strings.HasPrefix(d, "regexp:") { - pattern := d[7:] - re, err := regexp.Compile(pattern) - 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 - } + if strings.ToLower(domain) == d { + return false } } protocolString := result.Protocol() @@ -221,12 +263,12 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu protocolString = resComp.ProtocolForDomainResult() } for _, p := range request.OverrideDestinationForProtocol { - if strings.HasPrefix(protocolString, p) || strings.HasPrefix(p, protocolString) { + if strings.HasPrefix(protocolString, p) { return true } if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" && - fkr0.IsIPInIPPool(destination.Address) { - errors.LogInfo(ctx, "Using sniffer ", protocolString, " since the fake DNS missed") + destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) { + newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx)) return true } if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok { @@ -244,14 +286,10 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin if !destination.IsValid() { panic("Dispatcher: Invalid destination.") } - outbounds := session.OutboundsFromContext(ctx) - if len(outbounds) == 0 { - outbounds = []*session.Outbound{{}} - ctx = session.ContextWithOutbounds(ctx, outbounds) + ob := &session.Outbound{ + Target: destination, } - ob := outbounds[len(outbounds)-1] - ob.OriginalTarget = destination - ob.Target = destination + ctx = session.ContextWithOutbound(ctx, ob) content := session.ContentFromContext(ctx) if content == nil { content = new(session.Content) @@ -259,7 +297,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin } sniffingRequest := content.SniffingRequest - inbound, outbound := d.getLink(ctx) + inbound, outbound := d.getLink(ctx, destination.Network, sniffingRequest) if !sniffingRequest.Enabled { go d.routedDispatch(ctx, outbound, destination) } else { @@ -274,17 +312,9 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin } if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) { domain := result.Domain() - errors.LogInfo(ctx, "sniffed domain: ", domain) + newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx)) destination.Address = net.ParseAddress(domain) - protocol := result.Protocol() - if resComp, ok := result.(SnifferResultComposite); ok { - protocol = resComp.ProtocolForDomainResult() - } - isFakeIP := false - if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(ob.Target.Address) { - isFakeIP = true - } - if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP { + if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" { ob.RouteTarget = destination } else { ob.Target = destination @@ -299,16 +329,12 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin // DispatchLink implements routing.Dispatcher. func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error { if !destination.IsValid() { - return errors.New("Dispatcher: Invalid destination.") + return newError("Dispatcher: Invalid destination.") } - outbounds := session.OutboundsFromContext(ctx) - if len(outbounds) == 0 { - outbounds = []*session.Outbound{{}} - ctx = session.ContextWithOutbounds(ctx, outbounds) + ob := &session.Outbound{ + Target: destination, } - ob := outbounds[len(outbounds)-1] - ob.OriginalTarget = destination - ob.Target = destination + ctx = session.ContextWithOutbound(ctx, ob) content := session.ContentFromContext(ctx) if content == nil { content = new(session.Content) @@ -316,42 +342,36 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De } sniffingRequest := content.SniffingRequest if !sniffingRequest.Enabled { - d.routedDispatch(ctx, outbound, destination) + go d.routedDispatch(ctx, outbound, destination) } else { - cReader := &cachedReader{ - reader: outbound.Reader.(*pipe.Reader), - } - outbound.Reader = cReader - result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network) - if err == nil { - content.Protocol = result.Protocol() - } - if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) { - domain := result.Domain() - errors.LogInfo(ctx, "sniffed domain: ", domain) - destination.Address = net.ParseAddress(domain) - protocol := result.Protocol() - if resComp, ok := result.(SnifferResultComposite); ok { - protocol = resComp.ProtocolForDomainResult() + go func() { + cReader := &cachedReader{ + reader: outbound.Reader.(*pipe.Reader), } - isFakeIP := false - if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(ob.Target.Address) { - isFakeIP = true + outbound.Reader = cReader + result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network) + if err == nil { + content.Protocol = result.Protocol() } - if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP { - ob.RouteTarget = destination - } else { - ob.Target = destination + if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) { + domain := result.Domain() + newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx)) + destination.Address = net.ParseAddress(domain) + if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" { + ob.RouteTarget = destination + } else { + ob.Target = destination + } } - } - d.routedDispatch(ctx, outbound, destination) + d.routedDispatch(ctx, outbound, destination) + }() } return nil } func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) { - payload := buf.NewWithSize(32767) + payload := buf.New() defer payload.Release() sniffer := NewSniffer(ctx) @@ -363,36 +383,26 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw } contentResult, contentErr := func() (SniffResult, error) { - cacheDeadline := 200 * time.Millisecond totalAttempt := 0 for { select { case <-ctx.Done(): return nil, ctx.Err() default: - cachingStartingTimeStamp := time.Now() - err := cReader.Cache(payload, cacheDeadline) - if err != nil { - return nil, err + totalAttempt++ + if totalAttempt > 2 { + return nil, errSniffingTimeout } - cachingTimeElapsed := time.Since(cachingStartingTimeStamp) - cacheDeadline -= cachingTimeElapsed + cReader.Cache(payload) if !payload.IsEmpty() { result, err := sniffer.Sniff(ctx, payload.Bytes(), network) - switch err { - case common.ErrNoClue: // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not - totalAttempt++ - case protocol.ErrProtoNeedMoreData: // Protocol Need More Data: protocol matches, but need more data to complete sniffing - // in this case, do not add totalAttempt(allow to read until timeout) - default: + if err != common.ErrNoClue { return result, err } - } else { - totalAttempt++ } - if totalAttempt >= 2 || cacheDeadline <= 0 { - return nil, errSniffingTimeout + if payload.IsFull() { + return nil, errUnknownContent } } } @@ -405,9 +415,21 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw } return contentResult, contentErr } + func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] + ob := session.OutboundFromContext(ctx) + if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() { + proxied := hosts.LookupHosts(ob.Target.String()) + if proxied != nil { + ro := ob.RouteTarget == destination + destination.Address = *proxied + if ro { + ob.RouteTarget = destination + } else { + ob.Target = destination + } + } + } var handler outbound.Handler @@ -418,10 +440,10 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport. ctx = session.SetForcedOutboundTagToContext(ctx, "") if h := d.ohm.GetHandler(forcedOutboundTag); h != nil { isPickRoute = 1 - errors.LogInfo(ctx, "taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]") + newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) handler = h } else { - errors.LogError(ctx, "non existing tag for platform initialized detour: ", forcedOutboundTag) + newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx)) common.Close(link.Writer) common.Interrupt(link.Reader) return @@ -431,17 +453,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport. outTag := route.GetOutboundTag() if h := d.ohm.GetHandler(outTag); h != nil { isPickRoute = 2 - if route.GetRuleTag() == "" { - errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]") - } else { - errors.LogInfo(ctx, "Hit route rule: [", route.GetRuleTag(), "] so taking detour [", outTag, "] for [", destination, "]") - } + newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) handler = h } else { - errors.LogWarning(ctx, "non existing outTag: ", outTag) + newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } } else { - errors.LogInfo(ctx, "default route for ", destination) + newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx)) } } @@ -450,13 +468,12 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport. } if handler == nil { - errors.LogInfo(ctx, "default outbound handler not exist") + newError("default outbound handler not exist").WriteToLog(session.ExportIDToError(ctx)) common.Close(link.Writer) common.Interrupt(link.Reader) return } - ob.Tag = handler.Tag() if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil { if tag := handler.Tag(); tag != "" { if inTag == "" { diff --git a/app/dispatcher/dispatcher.go b/app/dispatcher/dispatcher.go index 909218d5..2ae48c85 100644 --- a/app/dispatcher/dispatcher.go +++ b/app/dispatcher/dispatcher.go @@ -1 +1,3 @@ package dispatcher + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/app/dispatcher/errors.generated.go b/app/dispatcher/errors.generated.go new file mode 100644 index 00000000..e13ee608 --- /dev/null +++ b/app/dispatcher/errors.generated.go @@ -0,0 +1,9 @@ +package dispatcher + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/dispatcher/fakednssniffer.go b/app/dispatcher/fakednssniffer.go index bed90877..ad879daf 100644 --- a/app/dispatcher/fakednssniffer.go +++ b/app/dispatcher/fakednssniffer.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/core" @@ -23,16 +22,15 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) } if fakeDNSEngine == nil { - errNotInit := errors.New("FakeDNSEngine is not initialized, but such a sniffer is used").AtError() + errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError() return protocolSnifferWithMetadata{}, errNotInit } return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP { - domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address) + Target := session.OutboundFromContext(ctx).Target + if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP { + domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address) if domainFromFakeDNS != "" { - errors.LogInfo(ctx, "fake dns got domain: ", domainFromFakeDNS, " for ip: ", ob.Target.Address.String()) + newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx)) return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil } } @@ -40,7 +38,7 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil { ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt) if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok { - inPool := fkr0.IsIPInIPPool(ob.Target.Address) + inPool := fkr0.IsIPInIPPool(Target.Address) ipAddressInRangeValue.addressInRange = &inPool } } @@ -110,10 +108,10 @@ func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWit } return nil, common.ErrNoClue } - errors.LogDebug(ctx, "ip address not in fake dns range, return as is") + newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog() return nil, common.ErrNoClue } - errors.LogWarning(ctx, "fake dns sniffer did not set address in range option, assume false.") + newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog() return nil, common.ErrNoClue }, metadataSniffer: false, diff --git a/app/dispatcher/sniffer.go b/app/dispatcher/sniffer.go index d6acf0d9..e23277a7 100644 --- a/app/dispatcher/sniffer.go +++ b/app/dispatcher/sniffer.go @@ -4,9 +4,7 @@ import ( "context" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol/bittorrent" "github.com/xtls/xray-core/common/protocol/http" "github.com/xtls/xray-core/common/protocol/quic" @@ -36,7 +34,7 @@ type Sniffer struct { func NewSniffer(ctx context.Context) *Sniffer { ret := &Sniffer{ sniffer: []protocolSnifferWithMetadata{ - {func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b, c) }, false, net.Network_TCP}, + {func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP}, @@ -54,22 +52,19 @@ func NewSniffer(ctx context.Context) *Sniffer { return ret } -var errUnknownContent = errors.New("unknown content") +var errUnknownContent = newError("unknown content") func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) { var pendingSniffer []protocolSnifferWithMetadata for _, si := range s.sniffer { - protocolSniffer := si.protocolSniffer + s := si.protocolSniffer if si.metadataSniffer || si.network != network { continue } - result, err := protocolSniffer(c, payload) + result, err := s(c, payload) if err == common.ErrNoClue { pendingSniffer = append(pendingSniffer, si) continue - } else if err == protocol.ErrProtoNeedMoreData { // Sniffer protocol matched, but need more data to complete sniffing - s.sniffer = []protocolSnifferWithMetadata{si} - return nil, err } if err == nil && result != nil { diff --git a/app/dns/cache_controller.go b/app/dns/cache_controller.go deleted file mode 100644 index f23c414d..00000000 --- a/app/dns/cache_controller.go +++ /dev/null @@ -1,188 +0,0 @@ -package dns - -import ( - "context" - go_errors "errors" - "github.com/xtls/xray-core/common" - "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/task" - dns_feature "github.com/xtls/xray-core/features/dns" - "golang.org/x/net/dns/dnsmessage" - "sync" - "time" -) - -type CacheController struct { - sync.RWMutex - ips map[string]*record - pub *pubsub.Service - cacheCleanup *task.Periodic - name string - disableCache bool -} - -func NewCacheController(name string, disableCache bool) *CacheController { - c := &CacheController{ - name: name, - disableCache: disableCache, - ips: make(map[string]*record), - pub: pubsub.NewService(), - } - - c.cacheCleanup = &task.Periodic{ - Interval: time.Minute, - Execute: c.CacheCleanup, - } - return c -} - -// CacheCleanup clears expired items from cache -func (c *CacheController) CacheCleanup() error { - now := time.Now() - c.Lock() - defer c.Unlock() - - if len(c.ips) == 0 { - return errors.New("nothing to do. stopping...") - } - - 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 - } - } - - if len(c.ips) == 0 { - c.ips = make(map[string]*record) - } - - return nil -} - -func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) { - elapsed := time.Since(req.start) - - c.Lock() - rec, found := c.ips[req.domain] - if !found { - rec = &record{} - } - - switch req.reqType { - case dnsmessage.TypeA: - rec.A = ipRec - case dnsmessage.TypeAAAA: - rec.AAAA = ipRec - } - - errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed) - c.ips[req.domain] = rec - - switch req.reqType { - case dnsmessage.TypeA: - c.pub.Publish(req.domain+"4", nil) - if !c.disableCache { - _, _, err := rec.AAAA.getIPs() - if !go_errors.Is(err, errRecordNotFound) { - c.pub.Publish(req.domain+"6", nil) - } - } - case dnsmessage.TypeAAAA: - c.pub.Publish(req.domain+"6", nil) - if !c.disableCache { - _, _, err := rec.A.getIPs() - if !go_errors.Is(err, errRecordNotFound) { - c.pub.Publish(req.domain+"4", nil) - } - } - } - - c.Unlock() - common.Must(c.cacheCleanup.Start()) -} - -func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { - c.RLock() - record, found := c.ips[domain] - c.RUnlock() - - if !found { - return nil, 0, errRecordNotFound - } - - var errs []error - var allIPs []net.IP - var rTTL uint32 = dns_feature.DefaultTTL - - mergeReq := option.IPv4Enable && option.IPv6Enable - - if option.IPv4Enable { - ips, ttl, err := record.A.getIPs() - 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 := record.AAAA.getIPs() - 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 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) { - // ipv4 and ipv6 belong to different subscription groups - if option.IPv4Enable { - sub4 = c.pub.Subscribe(domain + "4") - } - if option.IPv6Enable { - sub6 = c.pub.Subscribe(domain + "6") - } - return -} - -func closeSubscribers(sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) { - if sub4 != nil { - sub4.Close() - } - if sub6 != nil { - sub6.Close() - } -} diff --git a/app/dns/config.go b/app/dns/config.go index ab547e14..6236f7b5 100644 --- a/app/dns/config.go +++ b/app/dns/config.go @@ -1,7 +1,6 @@ package dns import ( - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/strmatcher" "github.com/xtls/xray-core/common/uuid" @@ -37,11 +36,11 @@ var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{ func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) { strMType, f := typeMap[t] if !f { - return nil, errors.New("unknown mapping type", t).AtWarning() + return nil, newError("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 nil, newError("failed to create str matcher").Base(err) } return matcher, nil } @@ -52,7 +51,7 @@ func toNetIP(addrs []net.Address) ([]net.IP, error) { if addr.Family().IsIP() { ips = append(ips, addr.IP()) } else { - return nil, errors.New("Failed to convert address", addr, "to Net IP.").AtWarning() + return nil, newError("Failed to convert address", addr, "to Net IP.").AtWarning() } } return ips, nil diff --git a/app/dns/config.pb.go b/app/dns/config.pb.go index d54142ce..7474c04a 100644 --- a/app/dns/config.pb.go +++ b/app/dns/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/dns/config.proto package dns @@ -80,7 +80,6 @@ const ( QueryStrategy_USE_IP QueryStrategy = 0 QueryStrategy_USE_IP4 QueryStrategy = 1 QueryStrategy_USE_IP6 QueryStrategy = 2 - QueryStrategy_USE_SYS QueryStrategy = 3 ) // Enum value maps for QueryStrategy. @@ -89,13 +88,11 @@ var ( 0: "USE_IP", 1: "USE_IP4", 2: "USE_IP6", - 3: "USE_SYS", } QueryStrategy_value = map[string]int32{ "USE_IP": 0, "USE_IP4": 1, "USE_IP6": 2, - "USE_SYS": 3, } ) @@ -135,23 +132,17 @@ type NameServer struct { ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"` PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"` - ExpectedGeoip []*router.GeoIP `protobuf:"bytes,3,rep,name=expected_geoip,json=expectedGeoip,proto3" json:"expected_geoip,omitempty"` + Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"` OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"` - QueryStrategy QueryStrategy `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` - ActPrior bool `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"` - Tag string `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"` - TimeoutMs uint64 `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"` - DisableCache bool `protobuf:"varint,11,opt,name=disableCache,proto3" json:"disableCache,omitempty"` - FinalQuery bool `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"` - UnexpectedGeoip []*router.GeoIP `protobuf:"bytes,13,rep,name=unexpected_geoip,json=unexpectedGeoip,proto3" json:"unexpected_geoip,omitempty"` - ActUnprior bool `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"` } func (x *NameServer) Reset() { *x = NameServer{} - mi := &file_app_dns_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_dns_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *NameServer) String() string { @@ -162,7 +153,7 @@ func (*NameServer) ProtoMessage() {} func (x *NameServer) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -205,9 +196,9 @@ func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain { return nil } -func (x *NameServer) GetExpectedGeoip() []*router.GeoIP { +func (x *NameServer) GetGeoip() []*router.GeoIP { if x != nil { - return x.ExpectedGeoip + return x.Geoip } return nil } @@ -219,70 +210,24 @@ func (x *NameServer) GetOriginalRules() []*NameServer_OriginalRule { return nil } -func (x *NameServer) GetQueryStrategy() QueryStrategy { - if x != nil { - return x.QueryStrategy - } - return QueryStrategy_USE_IP -} - -func (x *NameServer) GetActPrior() bool { - if x != nil { - return x.ActPrior - } - return false -} - -func (x *NameServer) GetTag() string { - if x != nil { - return x.Tag - } - return "" -} - -func (x *NameServer) GetTimeoutMs() uint64 { - if x != nil { - return x.TimeoutMs - } - return 0 -} - -func (x *NameServer) GetDisableCache() bool { - if x != nil { - return x.DisableCache - } - return false -} - -func (x *NameServer) GetFinalQuery() bool { - if x != nil { - return x.FinalQuery - } - return false -} - -func (x *NameServer) GetUnexpectedGeoip() []*router.GeoIP { - if x != nil { - return x.UnexpectedGeoip - } - return nil -} - -func (x *NameServer) GetActUnprior() bool { - if x != nil { - return x.ActUnprior - } - return false -} - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Nameservers used by this DNS. Only traditional UDP servers are support at + // the moment. A special value 'localhost' as a domain address can be set to + // use DNS on local system. + // + // Deprecated: Do not use. + NameServers []*net.Endpoint `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"` // NameServer list used by this DNS client. - // 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"` + // Static hosts. Domain to IP. + // Deprecated. Use static_hosts. + // + // Deprecated: Do not use. + Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes // (IPv6). ClientIp []byte `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` @@ -298,9 +243,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_dns_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_dns_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -311,7 +258,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -326,6 +273,14 @@ func (*Config) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{1} } +// Deprecated: Do not use. +func (x *Config) GetNameServers() []*net.Endpoint { + if x != nil { + return x.NameServers + } + return nil +} + func (x *Config) GetNameServer() []*NameServer { if x != nil { return x.NameServer @@ -333,6 +288,14 @@ func (x *Config) GetNameServer() []*NameServer { return nil } +// Deprecated: Do not use. +func (x *Config) GetHosts() map[string]*net.IPOrDomain { + if x != nil { + return x.Hosts + } + return nil +} + func (x *Config) GetClientIp() []byte { if x != nil { return x.ClientIp @@ -393,9 +356,11 @@ type NameServer_PriorityDomain struct { 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) + if protoimpl.UnsafeEnabled { + mi := &file_app_dns_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *NameServer_PriorityDomain) String() string { @@ -406,7 +371,7 @@ func (*NameServer_PriorityDomain) ProtoMessage() {} func (x *NameServer_PriorityDomain) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -446,9 +411,11 @@ type NameServer_OriginalRule struct { 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) + if protoimpl.UnsafeEnabled { + mi := &file_app_dns_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *NameServer_OriginalRule) String() string { @@ -459,7 +426,7 @@ func (*NameServer_OriginalRule) ProtoMessage() {} func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -503,9 +470,11 @@ type Config_HostMapping struct { func (x *Config_HostMapping) Reset() { *x = Config_HostMapping{} - mi := &file_app_dns_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_dns_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config_HostMapping) String() string { @@ -515,8 +484,8 @@ func (x *Config_HostMapping) String() string { func (*Config_HostMapping) ProtoMessage() {} func (x *Config_HostMapping) ProtoReflect() protoreflect.Message { - mi := &file_app_dns_config_proto_msgTypes[4] - if x != nil { + mi := &file_app_dns_config_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -528,7 +497,7 @@ func (x *Config_HostMapping) ProtoReflect() protoreflect.Message { // Deprecated: Use Config_HostMapping.ProtoReflect.Descriptor instead. 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, 1} } func (x *Config_HostMapping) GetType() DomainMatchingType { @@ -564,109 +533,103 @@ var File_app_dns_config_proto protoreflect.FileDescriptor var file_app_dns_config_proto_rawDesc = []byte{ 0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, - 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, - 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x06, 0x0a, 0x0a, - 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 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, 0x45, 0x6e, - 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, - 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, - 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, - 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3d, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, - 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, - 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 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, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74, - 0x50, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x74, - 0x50, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e, - 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65, - 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65, - 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a, - 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e, - 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 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, 0x1a, 0x36, 0x0a, 0x0c, - 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, - 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, + 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, + 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, + 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xee, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 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, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, + 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, + 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, + 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, + 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, + 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x5e, + 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 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, 0x1a, 0x36, + 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, + 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xef, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x73, 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, 0x39, 0x0a, + 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 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, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 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, 0x55, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 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, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 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, + 0x35, 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, 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 ( @@ -682,35 +645,38 @@ func file_app_dns_config_proto_rawDescGZIP() []byte { } var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -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_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_app_dns_config_proto_goTypes = []interface{}{ (DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType (QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy (*NameServer)(nil), // 2: xray.app.dns.NameServer (*Config)(nil), // 3: xray.app.dns.Config (*NameServer_PriorityDomain)(nil), // 4: xray.app.dns.NameServer.PriorityDomain (*NameServer_OriginalRule)(nil), // 5: xray.app.dns.NameServer.OriginalRule - (*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 + nil, // 6: xray.app.dns.Config.HostsEntry + (*Config_HostMapping)(nil), // 7: xray.app.dns.Config.HostMapping + (*net.Endpoint)(nil), // 8: xray.common.net.Endpoint + (*router.GeoIP)(nil), // 9: xray.app.router.GeoIP + (*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain } var file_app_dns_config_proto_depIdxs = []int32{ - 7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint + 8, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint 4, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain - 8, // 2: xray.app.dns.NameServer.expected_geoip:type_name -> xray.app.router.GeoIP + 9, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP 5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule - 1, // 4: xray.app.dns.NameServer.query_strategy:type_name -> xray.app.dns.QueryStrategy - 8, // 5: xray.app.dns.NameServer.unexpected_geoip:type_name -> xray.app.router.GeoIP - 2, // 6: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer - 6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping + 8, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint + 2, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer + 6, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry + 7, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping 1, // 8: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy 0, // 9: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType - 0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 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 + 10, // 10: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain + 0, // 11: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_app_dns_config_proto_init() } @@ -718,13 +684,75 @@ func file_app_dns_config_proto_init() { if File_app_dns_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_dns_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NameServer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_dns_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_dns_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NameServer_PriorityDomain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_dns_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NameServer_OriginalRule); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_dns_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config_HostMapping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_dns_config_proto_rawDesc, NumEnums: 2, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/app/dns/config.proto b/app/dns/config.proto index 4317d0d7..e2059e38 100644 --- a/app/dns/config.proto +++ b/app/dns/config.proto @@ -6,6 +6,7 @@ option go_package = "github.com/xtls/xray-core/app/dns"; option java_package = "com.xray.app.dns"; option java_multiple_files = true; +import "common/net/address.proto"; import "common/net/destination.proto"; import "app/router/config.proto"; @@ -25,16 +26,8 @@ message NameServer { } repeated PriorityDomain prioritized_domain = 2; - repeated xray.app.router.GeoIP expected_geoip = 3; + repeated xray.app.router.GeoIP geoip = 3; repeated OriginalRule original_rules = 4; - QueryStrategy query_strategy = 7; - bool actPrior = 8; - string tag = 9; - uint64 timeoutMs = 10; - bool disableCache = 11; - bool finalQuery = 12; - repeated xray.app.router.GeoIP unexpected_geoip = 13; - bool actUnprior = 14; } enum DomainMatchingType { @@ -48,14 +41,21 @@ enum QueryStrategy { USE_IP = 0; USE_IP4 = 1; USE_IP6 = 2; - USE_SYS = 3; } message Config { + // Nameservers used by this DNS. Only traditional UDP servers are support at + // the moment. A special value 'localhost' as a domain address can be set to + // use DNS on local system. + repeated xray.common.net.Endpoint NameServers = 1 [deprecated = true]; + // NameServer list used by this DNS client. - // A special value 'localhost' as a domain address can be set to use DNS on local system. repeated NameServer name_server = 5; + // Static hosts. Domain to IP. + // Deprecated. Use static_hosts. + map Hosts = 2 [deprecated = true]; + // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes // (IPv6). bytes client_ip = 3; diff --git a/app/dns/dns.go b/app/dns/dns.go index ddf4ac2e..af5f285b 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -1,25 +1,29 @@ // Package dns is an implementation of core.DNS feature. package dns +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - go_errors "errors" "fmt" - "sort" "strings" "sync" + "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/strmatcher" + "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/dns" ) // DNS is a DNS rely server. type DNS struct { sync.Mutex + tag string + disableCache bool disableFallback bool disableFallbackIfMatch bool ipOption *dns.IPOption @@ -28,7 +32,6 @@ type DNS struct { ctx context.Context domainMatcher strmatcher.IndexMatcher matcherInfos []*DomainMatcherInfo - checkSystem bool } // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher @@ -39,59 +42,50 @@ type DomainMatcherInfo struct { // New creates a new DNS server with given configuration. func New(ctx context.Context, config *Config) (*DNS, error) { + var tag string + if len(config.Tag) > 0 { + tag = config.Tag + } else { + tag = generateRandomTag() + } + var clientIP net.IP switch len(config.ClientIp) { case 0, net.IPv4len, net.IPv6len: clientIP = net.IP(config.ClientIp) default: - return nil, errors.New("unexpected client IP length ", len(config.ClientIp)) + return nil, newError("unexpected client IP length ", len(config.ClientIp)) } - var ipOption dns.IPOption - checkSystem := false + var ipOption *dns.IPOption switch config.QueryStrategy { case QueryStrategy_USE_IP: - ipOption = dns.IPOption{ + ipOption = &dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, } - case QueryStrategy_USE_SYS: - ipOption = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - } - checkSystem = true case QueryStrategy_USE_IP4: - ipOption = dns.IPOption{ + ipOption = &dns.IPOption{ IPv4Enable: true, IPv6Enable: false, FakeEnable: false, } case QueryStrategy_USE_IP6: - ipOption = dns.IPOption{ + ipOption = &dns.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: false, } - default: - return nil, errors.New("unexpected query strategy ", config.QueryStrategy) } - hosts, err := NewStaticHosts(config.StaticHosts) + hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts) if err != nil { - return nil, errors.New("failed to create hosts").Base(err) + return nil, newError("failed to create hosts").Base(err) } - var clients []*Client + clients := []*Client{} domainRuleCount := 0 - - var defaultTag = config.Tag - if len(config.Tag) == 0 { - defaultTag = generateRandomTag() - } - for _, ns := range config.NameServer { domainRuleCount += len(ns.PrioritizedDomain) } @@ -99,6 +93,16 @@ func New(ctx context.Context, config *Config) (*DNS, error) { // 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{} + geoipContainer := router.GeoIPMatcherContainer{} + + for _, endpoint := range config.NameServers { + features.PrintDeprecatedFeatureWarning("simple DNS server") + client, err := NewSimpleClient(ctx, endpoint, clientIP) + if err != nil { + return nil, newError("failed to create client").Base(err) + } + clients = append(clients, client) + } for _, ns := range config.NameServer { clientIdx := len(clients) @@ -116,40 +120,29 @@ func New(ctx context.Context, config *Config) (*DNS, error) { case net.IPv4len, net.IPv6len: myClientIP = net.IP(ns.ClientIp) } - - disableCache := config.DisableCache || ns.DisableCache - - var tag = defaultTag - if len(ns.Tag) > 0 { - tag = ns.Tag - } - clientIPOption := ResolveIpOptionOverride(ns.QueryStrategy, ipOption) - if !clientIPOption.IPv4Enable && !clientIPOption.IPv6Enable { - return nil, errors.New("no QueryStrategy available for ", ns.Address) - } - - client, err := NewClient(ctx, ns, myClientIP, disableCache, tag, clientIPOption, &matcherInfos, updateDomain) + client, err := NewClient(ctx, ns, myClientIP, geoipContainer, &matcherInfos, updateDomain) if err != nil { - return nil, errors.New("failed to create client").Base(err) + return nil, newError("failed to create client").Base(err) } clients = append(clients, client) } // If there is no DNS client in config, add a `localhost` DNS client if len(clients) == 0 { - clients = append(clients, NewLocalDNSClient(ipOption)) + clients = append(clients, NewLocalDNSClient()) } return &DNS{ + tag: tag, hosts: hosts, - ipOption: &ipOption, + ipOption: ipOption, clients: clients, ctx: ctx, domainMatcher: domainMatcher, matcherInfos: matcherInfos, + disableCache: config.DisableCache, disableFallback: config.DisableFallback, disableFallbackIfMatch: config.DisableFallbackIfMatch, - checkSystem: checkSystem, }, nil } @@ -171,36 +164,25 @@ func (s *DNS) Close() error { // IsOwnLink implements proxy.dns.ownLinkVerifier func (s *DNS) IsOwnLink(ctx context.Context) bool { inbound := session.InboundFromContext(ctx) - if inbound == nil { - return false - } - for _, client := range s.clients { - if client.tag == inbound.Tag { - return true - } - } - return false + return inbound != nil && inbound.Tag == s.tag } // LookupIP implements dns.Client. -func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, error) { - // Normalize the FQDN form query - domain = strings.TrimSuffix(domain, ".") +func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { if domain == "" { - return nil, 0, errors.New("empty domain name") + return nil, newError("empty domain name") } - if s.checkSystem { - supportIPv4, supportIPv6 := checkSystemNetwork() - option.IPv4Enable = option.IPv4Enable && supportIPv4 - option.IPv6Enable = option.IPv6Enable && supportIPv6 - } else { - option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable - option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable - } + option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable + option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable if !option.IPv4Enable && !option.IPv6Enable { - return nil, 0, dns.ErrEmptyResponse + return nil, dns.ErrEmptyResponse + } + + // Normalize the FQDN form query + if strings.HasSuffix(domain, ".") { + domain = domain[:len(domain)-1] } // Static host lookup @@ -208,59 +190,69 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er case addrs == nil: // Domain not recorded in static host break case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled) - return nil, 0, dns.ErrEmptyResponse + return nil, dns.ErrEmptyResponse case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement - errors.LogInfo(s.ctx, "domain replaced: ", domain, " -> ", addrs[0].Domain()) + newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog() domain = addrs[0].Domain() default: // Successfully found ip records in static host - errors.LogInfo(s.ctx, "returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs) - ips, err := toNetIP(addrs) - if err != nil { - return nil, 0, err - } - return ips, 10, nil // Hosts ttl is 10 + newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog() + return toNetIP(addrs) } // Name servers lookup - var errs []error + errs := []error{} + ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag}) 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()) + newError("skip DNS resolution for domain ", domain, " at server ", client.Name()).AtDebug().WriteToLog() continue } - - ips, ttl, err := client.QueryIP(s.ctx, domain, option) - + ips, err := client.QueryIP(ctx, domain, option, s.disableCache) if len(ips) > 0 { - if ttl == 0 { - ttl = 1 - } - return ips, ttl, nil + return ips, nil } - - errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name()) - if err == nil { - err = dns.ErrEmptyResponse + if err != nil { + newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog() + errs = append(errs, err) } - errs = append(errs, err) - - if client.IsFinalQuery() { - break + if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch { + return nil, err } } - 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, newError("returning nil for domain ", domain).Base(errors.Combine(errs...)) +} + +// LookupHosts implements dns.HostsLookup. +func (s *DNS) LookupHosts(domain string) *net.Address { + domain = strings.TrimSuffix(domain, ".") + if domain == "" { + return nil } - return nil, 0, dns.ErrEmptyResponse + // Normalize the FQDN form query + addrs := s.hosts.Lookup(domain, *s.ipOption) + if len(addrs) > 0 { + newError("domain replaced: ", domain, " -> ", addrs[0].String()).AtInfo().WriteToLog() + return &addrs[0] + } + + return nil +} + +// GetIPOption implements ClientWithIPOption. +func (s *DNS) GetIPOption() *dns.IPOption { + return s.ipOption +} + +// SetQueryOption implements ClientWithIPOption. +func (s *DNS) SetQueryOption(isIPv4Enable, isIPv6Enable bool) { + s.ipOption.IPv4Enable = isIPv4Enable + s.ipOption.IPv6Enable = isIPv6Enable +} + +// SetFakeDNSOption implements ClientWithIPOption. +func (s *DNS) SetFakeDNSOption(isFakeEnable bool) { + s.ipOption.FakeEnable = isFakeEnable } func (s *DNS) sortClients(domain string) []*Client { @@ -271,11 +263,7 @@ func (s *DNS) sortClients(domain string) []*Client { // Priority domain matching hasMatch := false - MatchSlice := s.domainMatcher.Match(domain) - sort.Slice(MatchSlice, func(i, j int) bool { - return MatchSlice[i] < MatchSlice[j] - }) - for _, match := range MatchSlice { + for _, match := range s.domainMatcher.Match(domain) { info := s.matcherInfos[match] client := s.clients[info.clientIdx] domainRule := client.domains[info.domainRuleIdx] @@ -302,16 +290,16 @@ func (s *DNS) sortClients(domain string) []*Client { } if len(domainRules) > 0 { - errors.LogDebug(s.ctx, "domain ", domain, " matches following rules: ", domainRules) + newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog() } if len(clientNames) > 0 { - errors.LogDebug(s.ctx, "domain ", domain, " will use DNS in order: ", clientNames) + newError("domain ", domain, " will use DNS in order: ", clientNames).AtDebug().WriteToLog() } if len(clients) == 0 { clients = append(clients, s.clients[0]) clientNames = append(clientNames, s.clients[0].Name()) - errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames) + newError("domain ", domain, " will use the first DNS: ", clientNames).AtDebug().WriteToLog() } return clients @@ -322,22 +310,3 @@ func init() { return New(ctx, config.(*Config)) })) } - -func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) { - conn4, err4 := net.Dial("udp4", "8.8.8.8:53") - if err4 != nil { - supportIPv4 = false - } else { - supportIPv4 = true - conn4.Close() - } - - conn6, err6 := net.Dial("udp6", "[2001:4860:4860::8888]:53") - if err6 != nil { - supportIPv6 = false - } else { - supportIPv6 = true - conn6.Close() - } - return -} diff --git a/app/dns/dns_test.go b/app/dns/dns_test.go index cb70b0b3..b3a8def8 100644 --- a/app/dns/dns_test.go +++ b/app/dns/dns_test.go @@ -13,7 +13,6 @@ import ( _ "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/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/core" @@ -76,9 +75,6 @@ func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { case q.Name == "notexist.google.com." && q.Qtype == dns.TypeAAAA: ans.MsgHdr.Rcode = dns.RcodeNameError - case q.Name == "notexist.google.com." && q.Qtype == dns.TypeA: - ans.MsgHdr.Rcode = dns.RcodeNameError - case q.Name == "hostname." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("hostname. IN A 127.0.0.1") ans.Answer = append(ans.Answer, rr) @@ -120,23 +116,22 @@ func TestUDPServerSubnet(t *testing.T) { Handler: &staticHandler{}, UDPSize: 1200, } + go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&Config{ - NameServer: []*NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: uint32(port), }, + Port: uint32(port), }, }, ClientIp: []byte{7, 8, 9, 10}, @@ -157,7 +152,7 @@ func TestUDPServerSubnet(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) - ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -187,17 +182,15 @@ func TestUDPServer(t *testing.T) { config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&Config{ - NameServer: []*NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: uint32(port), }, + Port: uint32(port), }, }, }), @@ -218,7 +211,7 @@ func TestUDPServer(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -233,7 +226,7 @@ func TestUDPServer(t *testing.T) { } { - ips, _, err := client.LookupIP("facebook.com", feature_dns.IPOption{ + ips, err := client.LookupIP("facebook.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -248,7 +241,7 @@ func TestUDPServer(t *testing.T) { } { - _, _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{ + _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -262,12 +255,12 @@ func TestUDPServer(t *testing.T) { } { - ips, _, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: false, }) - if !errors.AllEqual(feature_dns.ErrEmptyResponse, errors.Cause(err)) { + if err != feature_dns.ErrEmptyResponse { t.Fatal("error: ", err) } if len(ips) != 0 { @@ -278,7 +271,7 @@ func TestUDPServer(t *testing.T) { dnsServer.Shutdown() { - ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -309,18 +302,18 @@ func TestPrioritizedDomain(t *testing.T) { config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&Config{ - NameServer: []*NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: 9999, /* unreachable */ }, + Port: 9999, /* unreachable */ }, + }, + NameServer: []*NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, @@ -359,7 +352,7 @@ func TestPrioritizedDomain(t *testing.T) { startTime := time.Now() { - ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -395,17 +388,15 @@ func TestUDPServerIPv6(t *testing.T) { config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&Config{ - NameServer: []*NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: uint32(port), }, + Port: uint32(port), }, }, }), @@ -425,7 +416,7 @@ func TestUDPServerIPv6(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, _, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: false, @@ -456,17 +447,15 @@ func TestStaticHostDomain(t *testing.T) { config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&Config{ - NameServer: []*NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: uint32(port), }, + Port: uint32(port), }, }, StaticHosts: []*Config_HostMapping{ @@ -494,7 +483,7 @@ func TestStaticHostDomain(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, _, err := client.LookupIP("example.com", feature_dns.IPOption{ + ips, err := client.LookupIP("example.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -539,7 +528,7 @@ func TestIPMatch(t *testing.T) { }, Port: uint32(port), }, - ExpectedGeoip: []*router.GeoIP{ + Geoip: []*router.GeoIP{ { CountryCode: "local", Cidr: []*router.CIDR{ @@ -563,7 +552,7 @@ func TestIPMatch(t *testing.T) { }, Port: uint32(port), }, - ExpectedGeoip: []*router.GeoIP{ + Geoip: []*router.GeoIP{ { CountryCode: "test", Cidr: []*router.CIDR{ @@ -605,7 +594,7 @@ func TestIPMatch(t *testing.T) { startTime := time.Now() { - ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -641,18 +630,18 @@ func TestLocalDomain(t *testing.T) { config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&Config{ - NameServer: []*NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: 9999, /* unreachable */ }, + Port: 9999, /* unreachable */ }, + }, + NameServer: []*NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, @@ -667,7 +656,7 @@ func TestLocalDomain(t *testing.T) { // Equivalent of dotless:localhost {Type: DomainMatchingType_Regex, Domain: "^[^.]*localhost[^.]*$"}, }, - ExpectedGeoip: []*router.GeoIP{ + Geoip: []*router.GeoIP{ { // Will match localhost, localhost-a and localhost-b, CountryCode: "local", Cidr: []*router.CIDR{ @@ -728,7 +717,7 @@ func TestLocalDomain(t *testing.T) { startTime := time.Now() { // Will match dotless: - ips, _, err := client.LookupIP("hostname", feature_dns.IPOption{ + ips, err := client.LookupIP("hostname", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -743,7 +732,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match domain:local - ips, _, err := client.LookupIP("hostname.local", feature_dns.IPOption{ + ips, err := client.LookupIP("hostname.local", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -758,7 +747,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match static ip - ips, _, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{ + ips, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -773,7 +762,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match domain replacing - ips, _, err := client.LookupIP("hostnamealias", feature_dns.IPOption{ + ips, err := client.LookupIP("hostnamealias", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -787,8 +776,8 @@ func TestLocalDomain(t *testing.T) { } } - { // Will match dotless:localhost, but not expectedIPs: 127.0.0.2, 127.0.0.3, then matches at dotless: - ips, _, err := client.LookupIP("localhost", feature_dns.IPOption{ + { // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless: + ips, err := client.LookupIP("localhost", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -802,8 +791,8 @@ func TestLocalDomain(t *testing.T) { } } - { // Will match dotless:localhost, and expectedIPs: 127.0.0.2, 127.0.0.3 - ips, _, err := client.LookupIP("localhost-a", feature_dns.IPOption{ + { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 + ips, err := client.LookupIP("localhost-a", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -817,8 +806,8 @@ func TestLocalDomain(t *testing.T) { } } - { // Will match dotless:localhost, and expectedIPs: 127.0.0.2, 127.0.0.3 - ips, _, err := client.LookupIP("localhost-b", feature_dns.IPOption{ + { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 + ips, err := client.LookupIP("localhost-b", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -833,7 +822,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless: - ips, _, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{ + ips, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -869,18 +858,18 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&Config{ - NameServer: []*NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: 9999, /* unreachable */ }, + Port: 9999, /* unreachable */ }, + }, + NameServer: []*NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, @@ -897,7 +886,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { Domain: "google.com", }, }, - ExpectedGeoip: []*router.GeoIP{ + Geoip: []*router.GeoIP{ { // Will only match 8.8.8.8 and 8.8.4.4 Cidr: []*router.CIDR{ {Ip: []byte{8, 8, 8, 8}, Prefix: 32}, @@ -922,7 +911,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { Domain: "google.com", }, }, - ExpectedGeoip: []*router.GeoIP{ + Geoip: []*router.GeoIP{ { // Will match 8.8.8.8 and 8.8.8.7, etc Cidr: []*router.CIDR{ {Ip: []byte{8, 8, 8, 7}, Prefix: 24}, @@ -946,7 +935,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { Domain: "api.google.com", }, }, - ExpectedGeoip: []*router.GeoIP{ + Geoip: []*router.GeoIP{ { // Will only match 8.8.7.7 (api.google.com) Cidr: []*router.CIDR{ {Ip: []byte{8, 8, 7, 7}, Prefix: 32}, @@ -970,7 +959,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { Domain: "v2.api.google.com", }, }, - ExpectedGeoip: []*router.GeoIP{ + Geoip: []*router.GeoIP{ { // Will only match 8.8.7.8 (v2.api.google.com) Cidr: []*router.CIDR{ {Ip: []byte{8, 8, 7, 8}, Prefix: 32}, @@ -999,7 +988,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { startTime := time.Now() { // Will match server 1,2 and server 1 returns expected ip - ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -1014,7 +1003,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one - ips, _, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: false, FakeEnable: false, @@ -1029,7 +1018,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 3,1,2 and server 3 returns expected one - ips, _, err := client.LookupIP("api.google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("api.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -1044,7 +1033,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 4,3,1,2 and server 4 returns expected one - ips, _, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{ + ips, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, diff --git a/app/dns/dnscommon.go b/app/dns/dnscommon.go index 0bd712ff..df1b17af 100644 --- a/app/dns/dnscommon.go +++ b/app/dns/dnscommon.go @@ -31,31 +31,30 @@ type record struct { // IPRecord is a cacheable item for a resolved domain type IPRecord struct { - ReqID uint16 - IP []net.IP - Expire time.Time - RCode dnsmessage.RCode - RawHeader *dnsmessage.Header + ReqID uint16 + IP []net.Address + Expire time.Time + RCode dnsmessage.RCode } -func (r *IPRecord) getIPs() ([]net.IP, uint32, error) { - if r == nil { - return nil, 0, errRecordNotFound +func (r *IPRecord) getIPs() ([]net.Address, error) { + if r == nil || r.Expire.Before(time.Now()) { + return nil, errRecordNotFound } - untilExpire := time.Until(r.Expire) - if untilExpire <= 0 { - return nil, 0, errRecordNotFound - } - - ttl := uint32(untilExpire/time.Second) + uint32(1) if r.RCode != dnsmessage.RCodeSuccess { - return nil, ttl, dns_feature.RCodeError(r.RCode) - } - if len(r.IP) == 0 { - return nil, ttl, dns_feature.ErrEmptyResponse + return nil, dns_feature.RCodeError(r.RCode) } + return r.IP, nil +} - return r.IP, ttl, nil +func isNewer(baseRec *IPRecord, newRec *IPRecord) bool { + if newRec == nil { + return false + } + if baseRec == nil { + return true + } + return baseRec.Expire.Before(newRec.Expire) } var errRecordNotFound = errors.New("record not found") @@ -68,59 +67,49 @@ type dnsRequest struct { msg *dnsmessage.Message } -func genEDNS0Options(clientIP net.IP, padding int) *dnsmessage.Resource { - if len(clientIP) == 0 && padding == 0 { +func genEDNS0Options(clientIP net.IP) *dnsmessage.Resource { + if len(clientIP) == 0 { return nil } - const EDNS0SUBNET = 0x8 - const EDNS0PADDING = 0xc + var netmask int + var family uint16 + + if len(clientIP) == 4 { + family = 1 + netmask = 24 // 24 for IPV4, 96 for IPv6 + } else { + family = 2 + netmask = 96 + } + + b := make([]byte, 4) + binary.BigEndian.PutUint16(b[0:], family) + b[2] = byte(netmask) + b[3] = 0 + switch family { + case 1: + ip := clientIP.To4().Mask(net.CIDRMask(netmask, net.IPv4len*8)) + needLength := (netmask + 8 - 1) / 8 // division rounding up + b = append(b, ip[:needLength]...) + case 2: + ip := clientIP.Mask(net.CIDRMask(netmask, net.IPv6len*8)) + needLength := (netmask + 8 - 1) / 8 // division rounding up + b = append(b, ip[:needLength]...) + } + + const EDNS0SUBNET = 0x08 opt := new(dnsmessage.Resource) common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true)) - body := dnsmessage.OPTResource{} - opt.Body = &body - if len(clientIP) != 0 { - var netmask int - var family uint16 - - if len(clientIP) == 4 { - family = 1 - netmask = 24 // 24 for IPV4, 96 for IPv6 - } else { - family = 2 - netmask = 96 - } - - b := make([]byte, 4) - binary.BigEndian.PutUint16(b[0:], family) - b[2] = byte(netmask) - b[3] = 0 - switch family { - case 1: - ip := clientIP.To4().Mask(net.CIDRMask(netmask, net.IPv4len*8)) - needLength := (netmask + 8 - 1) / 8 // division rounding up - b = append(b, ip[:needLength]...) - case 2: - ip := clientIP.Mask(net.CIDRMask(netmask, net.IPv6len*8)) - needLength := (netmask + 8 - 1) / 8 // division rounding up - b = append(b, ip[:needLength]...) - } - - body.Options = append(body.Options, - dnsmessage.Option{ + opt.Body = &dnsmessage.OPTResource{ + Options: []dnsmessage.Option{ + { Code: EDNS0SUBNET, Data: b, - }) - } - - if padding != 0 { - body.Options = append(body.Options, - dnsmessage.Option{ - Code: EDNS0PADDING, - Data: make([]byte, padding), - }) + }, + }, } return opt @@ -182,18 +171,17 @@ func parseResponse(payload []byte) (*IPRecord, error) { var parser dnsmessage.Parser h, err := parser.Start(payload) if err != nil { - return nil, errors.New("failed to parse DNS response").Base(err).AtWarning() + return nil, newError("failed to parse DNS response").Base(err).AtWarning() } if err := parser.SkipAllQuestions(); err != nil { - return nil, errors.New("failed to skip questions in DNS response").Base(err).AtWarning() + return nil, newError("failed to skip questions in DNS response").Base(err).AtWarning() } now := time.Now() ipRecord := &IPRecord{ - ReqID: h.ID, - RCode: h.RCode, - Expire: now.Add(time.Second * dns_feature.DefaultTTL), - RawHeader: &h, + ReqID: h.ID, + RCode: h.RCode, + Expire: now.Add(time.Second * 600), } L: @@ -201,14 +189,14 @@ L: ah, err := parser.AnswerHeader() if err != nil { if err != dnsmessage.ErrSectionDone { - errors.LogInfoInner(context.Background(), err, "failed to parse answer section for domain: ", ah.Name.String()) + newError("failed to parse answer section for domain: ", ah.Name.String()).Base(err).WriteToLog() } break } ttl := ah.TTL if ttl == 0 { - ttl = 1 + ttl = 600 } expire := now.Add(time.Duration(ttl) * time.Second) if ipRecord.Expire.After(expire) { @@ -219,23 +207,20 @@ L: case dnsmessage.TypeA: ans, err := parser.AResource() if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to parse A record for domain: ", ah.Name) + newError("failed to parse A record for domain: ", ah.Name).Base(err).WriteToLog() break L } - ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.A[:]).IP()) + ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.A[:])) case dnsmessage.TypeAAAA: ans, err := parser.AAAAResource() if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to parse AAAA record for domain: ", ah.Name) + newError("failed to parse AAAA record for domain: ", ah.Name).Base(err).WriteToLog() break L } - newIP := net.IPAddress(ans.AAAA[:]).IP() - if len(newIP) == net.IPv6len { - ipRecord.IP = append(ipRecord.IP, newIP) - } + ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.AAAA[:])) default: if err := parser.SkipAnswer(); err != nil { - errors.LogInfoInner(context.Background(), err, "failed to skip answer") + newError("failed to skip answer").Base(err).WriteToLog() break L } continue diff --git a/app/dns/dnscommon_test.go b/app/dns/dnscommon_test.go index bbaa9a21..6530d1a0 100644 --- a/app/dns/dnscommon_test.go +++ b/app/dns/dnscommon_test.go @@ -51,7 +51,7 @@ func Test_parseResponse(t *testing.T) { }{ { "empty", - &IPRecord{0, []net.IP(nil), time.Time{}, dnsmessage.RCodeSuccess, nil}, + &IPRecord{0, []net.Address(nil), time.Time{}, dnsmessage.RCodeSuccess}, false, }, { @@ -63,16 +63,15 @@ func Test_parseResponse(t *testing.T) { "a record", &IPRecord{ 1, - []net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")}, + []net.Address{net.ParseAddress("8.8.8.8"), net.ParseAddress("8.8.4.4")}, time.Time{}, dnsmessage.RCodeSuccess, - nil, }, false, }, { "aaaa record", - &IPRecord{2, []net.IP{net.ParseIP("2001::123:8888"), net.ParseIP("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess, nil}, + &IPRecord{2, []net.Address{net.ParseAddress("2001::123:8888"), net.ParseAddress("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess}, false, }, } @@ -85,12 +84,11 @@ func Test_parseResponse(t *testing.T) { } if got != nil { - // reset the time and RawHeader + // reset the time got.Expire = time.Time{} - got.RawHeader = nil } if cmp.Diff(got, tt.want) != "" { - t.Error(cmp.Diff(got, tt.want)) + t.Errorf(cmp.Diff(got, tt.want)) // t.Errorf("handleResponse() = %#v, want %#v", got, tt.want) } }) @@ -156,7 +154,7 @@ func Test_genEDNS0Options(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := genEDNS0Options(tt.args.clientIP, 0); got == nil { + if got := genEDNS0Options(tt.args.clientIP); got == nil { t.Errorf("genEDNS0Options() = %v, want %v", got, tt.want) } }) diff --git a/app/dns/errors.generated.go b/app/dns/errors.generated.go new file mode 100644 index 00000000..d7375a9b --- /dev/null +++ b/app/dns/errors.generated.go @@ -0,0 +1,9 @@ +package dns + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/dns/fakedns/errors.generated.go b/app/dns/fakedns/errors.generated.go new file mode 100644 index 00000000..84e0448c --- /dev/null +++ b/app/dns/fakedns/errors.generated.go @@ -0,0 +1,9 @@ +package fakedns + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/dns/fakedns/fake.go b/app/dns/fakedns/fake.go index 20e4bffa..e5550074 100644 --- a/app/dns/fakedns/fake.go +++ b/app/dns/fakedns/fake.go @@ -10,7 +10,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/cache" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/dns" ) @@ -46,7 +45,7 @@ func (fkdns *Holder) Start() error { if fkdns.config != nil && fkdns.config.IpPool != "" && fkdns.config.LruSize != 0 { return fkdns.initializeFromConfig() } - return errors.New("invalid fakeDNS setting") + return newError("invalid fakeDNS setting") } func (fkdns *Holder) Close() error { @@ -61,7 +60,7 @@ func NewFakeDNSHolder() (*Holder, error) { var err error if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil { - return nil, errors.New("Unable to create Fake Dns Engine").Base(err).AtError() + return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError() } err = fkdns.initialize(dns.FakeIPv4Pool, 65535) if err != nil { @@ -83,13 +82,13 @@ func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error { var err error if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil { - return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError() + return newError("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError() } ones, bits := ipRange.Mask.Size() rooms := bits - ones if math.Log2(float64(lruSize)) >= float64(rooms) { - return errors.New("LRU size is bigger than subnet size").AtError() + return newError("LRU size is bigger than subnet size").AtError() } fkdns.domainToIP = cache.NewLru(lruSize) fkdns.ipRange = ipRange @@ -138,7 +137,7 @@ func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string { if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok { return k.(string) } - errors.LogInfo(context.Background(), "A fake ip request to ", ip, ", however there is no matching domain name in fake DNS") + newError("A fake ip request to ", ip, ", however there is no matching domain name in fake DNS").AtInfo().WriteToLog() return "" } @@ -193,10 +192,10 @@ func (h *HolderMulti) Start() error { for _, v := range h.holders { if v.config != nil && v.config.IpPool != "" && v.config.LruSize != 0 { if err := v.Start(); err != nil { - return errors.New("Cannot start all fake dns pools").Base(err) + return newError("Cannot start all fake dns pools").Base(err) } } else { - return errors.New("invalid fakeDNS setting") + return newError("invalid fakeDNS setting") } } return nil @@ -205,7 +204,7 @@ func (h *HolderMulti) Start() error { func (h *HolderMulti) Close() error { for _, v := range h.holders { if err := v.Close(); err != nil { - return errors.New("Cannot close all fake dns pools").Base(err) + return newError("Cannot close all fake dns pools").Base(err) } } return nil diff --git a/app/dns/fakedns/fakedns.go b/app/dns/fakedns/fakedns.go index 05da03a5..710b8a6f 100644 --- a/app/dns/fakedns/fakedns.go +++ b/app/dns/fakedns/fakedns.go @@ -1 +1,3 @@ package fakedns + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/app/dns/fakedns/fakedns.pb.go b/app/dns/fakedns/fakedns.pb.go index 6160eaee..8cf02aee 100644 --- a/app/dns/fakedns/fakedns.pb.go +++ b/app/dns/fakedns/fakedns.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/dns/fakedns/fakedns.proto package fakedns @@ -31,9 +31,11 @@ type FakeDnsPool struct { func (x *FakeDnsPool) Reset() { *x = FakeDnsPool{} - mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *FakeDnsPool) String() string { @@ -44,7 +46,7 @@ func (*FakeDnsPool) ProtoMessage() {} func (x *FakeDnsPool) ProtoReflect() protoreflect.Message { mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -83,9 +85,11 @@ type FakeDnsPoolMulti struct { func (x *FakeDnsPoolMulti) Reset() { *x = FakeDnsPoolMulti{} - mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *FakeDnsPoolMulti) String() string { @@ -96,7 +100,7 @@ func (*FakeDnsPoolMulti) ProtoMessage() {} func (x *FakeDnsPoolMulti) ProtoReflect() protoreflect.Message { mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -155,7 +159,7 @@ func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte { } var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_app_dns_fakedns_fakedns_proto_goTypes = []any{ +var file_app_dns_fakedns_fakedns_proto_goTypes = []interface{}{ (*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool (*FakeDnsPoolMulti)(nil), // 1: xray.app.dns.fakedns.FakeDnsPoolMulti } @@ -173,6 +177,32 @@ func file_app_dns_fakedns_fakedns_proto_init() { if File_app_dns_fakedns_fakedns_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_dns_fakedns_fakedns_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FakeDnsPool); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_dns_fakedns_fakedns_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FakeDnsPoolMulti); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/dns/hosts.go b/app/dns/hosts.go index f4e06dbb..64413481 100644 --- a/app/dns/hosts.go +++ b/app/dns/hosts.go @@ -1,10 +1,10 @@ package dns import ( - "context" - "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/strmatcher" + "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/dns" ) @@ -15,17 +15,34 @@ type StaticHosts struct { } // NewStaticHosts creates a new StaticHosts instance. -func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) { +func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) { g := new(strmatcher.MatcherGroup) sh := &StaticHosts{ - ips: make([][]net.Address, len(hosts)+16), + ips: make([][]net.Address, len(hosts)+len(legacy)+16), matchers: g, } + if legacy != nil { + features.PrintDeprecatedFeatureWarning("simple host mapping") + + for domain, ip := range legacy { + matcher, err := strmatcher.Full.New(domain) + common.Must(err) + id := g.Add(matcher) + + address := ip.AsAddress() + if address.Family().IsDomain() { + return nil, newError("invalid domain address in static hosts: ", address.Domain()).AtWarning() + } + + sh.ips[id] = []net.Address{address} + } + } + for _, mapping := range hosts { matcher, err := toStrMatcher(mapping.Type, mapping.Domain) if err != nil { - return nil, errors.New("failed to create domain matcher").Base(err) + return nil, newError("failed to create domain matcher").Base(err) } id := g.Add(matcher) ips := make([]net.Address, 0, len(mapping.Ip)+1) @@ -36,10 +53,12 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) { for _, ip := range mapping.Ip { addr := net.IPAddress(ip) if addr == nil { - return nil, errors.New("invalid IP address in static hosts: ", ip).AtWarning() + return nil, newError("invalid IP address in static hosts: ", ip).AtWarning() } ips = append(ips, addr) } + default: + return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning() } sh.ips[id] = ips @@ -59,14 +78,9 @@ func filterIP(ips []net.Address, option dns.IPOption) []net.Address { } func (h *StaticHosts) lookupInternal(domain string) []net.Address { - ips := make([]net.Address, 0) - found := false + var ips []net.Address for _, id := range h.matchers.Match(domain) { ips = append(ips, h.ips[id]...) - found = true - } - if !found { - return nil } return ips } @@ -74,9 +88,9 @@ func (h *StaticHosts) lookupInternal(domain string) []net.Address { func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address { switch addrs := h.lookupInternal(domain); { case len(addrs) == 0: // Not recorded in static hosts, return nil - return addrs + return nil 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") + newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog() if maxDepth > 0 { unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1) if unwrapped != nil { diff --git a/app/dns/hosts_test.go b/app/dns/hosts_test.go index 380c7cb2..40533b11 100644 --- a/app/dns/hosts_test.go +++ b/app/dns/hosts_test.go @@ -50,7 +50,7 @@ func TestStaticHosts(t *testing.T) { }, } - hosts, err := NewStaticHosts(pb) + hosts, err := NewStaticHosts(pb, nil) common.Must(err) { diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index cf1b665b..d6f9b272 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -9,7 +9,6 @@ import ( "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/strmatcher" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/dns" @@ -21,27 +20,22 @@ type Server interface { // Name of the Client. Name() string // 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, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error) } // Client is the interface for DNS client. type Client struct { - server Server - skipFallback bool - domains []string - expectedIPs []*router.GeoIPMatcher - unexpectedIPs []*router.GeoIPMatcher - actPrior bool - actUnprior bool - tag string - timeoutMs time.Duration - finalQuery bool - ipOption *dns.IPOption - checkSystem bool + server Server + clientIP net.IP + skipFallback bool + domains []string + expectIPs []*router.GeoIPMatcher } +var errExpectedIPNonMatch = errors.New("expectIPs not match") + // 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, clientIP net.IP) (Server, error) { +func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, error) { if address := dest.Address; address.Family().IsDomain() { u, err := url.Parse(address.Domain()) if err != nil { @@ -50,66 +44,46 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis switch { case strings.EqualFold(u.String(), "localhost"): return NewLocalNameServer(), nil - case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode - return NewDoHNameServer(u, dispatcher, false, disableCache, clientIP), nil - case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode - return NewDoHNameServer(u, dispatcher, true, disableCache, clientIP), nil - case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode - return NewDoHNameServer(u, nil, false, disableCache, clientIP), nil - case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode - return NewDoHNameServer(u, nil, true, disableCache, clientIP), nil + case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode + return NewDoHNameServer(u, dispatcher) + case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode + return NewDoHLocalNameServer(u), nil case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode - return NewQUICNameServer(u, disableCache, clientIP) + return NewQUICNameServer(u) case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode - return NewTCPNameServer(u, dispatcher, disableCache, clientIP) + return NewTCPNameServer(u, dispatcher) case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode - return NewTCPLocalNameServer(u, disableCache, clientIP) + return NewTCPLocalNameServer(u) case strings.EqualFold(u.String(), "fakedns"): - var fd dns.FakeDNSEngine - err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { - fd = fdns - }) - if err != nil { - return nil, err - } - return NewFakeDNSServer(fd), nil + return NewFakeDNSServer(), nil } } if dest.Network == net.Network_Unknown { dest.Network = net.Network_UDP } if dest.Network == net.Network_UDP { // UDP classic DNS mode - return NewClassicNameServer(dest, dispatcher, disableCache, clientIP), nil + return NewClassicNameServer(dest, dispatcher), nil } - return nil, errors.New("No available name server could be created from ", dest).AtWarning() + return nil, newError("No available name server could be created from ", dest).AtWarning() } // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs. -func NewClient( - ctx context.Context, - ns *NameServer, - clientIP net.IP, - disableCache bool, - tag string, - ipOption dns.IPOption, - matcherInfos *[]*DomainMatcherInfo, - updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error, -) (*Client, error) { +func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, matcherInfos *[]*DomainMatcherInfo, updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error) (*Client, error) { client := &Client{} err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { // Create a new server for each client for now - server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, clientIP) + server, err := NewServer(ns.Address.AsDestination(), dispatcher) if err != nil { - return errors.New("failed to create nameserver").Base(err).AtWarning() + return newError("failed to create nameserver").Base(err).AtWarning() } - // Prioritize local domains with specific TLDs or those without any dot for the local DNS + // Priotize local domains with specific TLDs or without any dot to local DNS if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS { ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...) ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule) // The following lines is a solution to avoid core panics(rule index out of range) when setting `localhost` DNS client in config. - // Because the `localhost` DNS client will append len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule. + // Because the `localhost` DNS client will apend len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule. // But `matcherInfos` has no enough length to add rules, which leads to core panics (rule index out of range). // 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. // Related issues: @@ -130,7 +104,7 @@ func NewClient( 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() + return newError("failed to create prioritized domain").Base(err).AtWarning() } originalRuleIdx := ruleCurr if ruleCurr < len(ns.OriginalRules) { @@ -149,154 +123,98 @@ func NewClient( } err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos) if err != nil { - return errors.New("failed to create prioritized domain").Base(err).AtWarning() + return newError("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) + var matchers []*router.GeoIPMatcher + for _, geoip := range ns.Geoip { + matcher, err := container.Add(geoip) if err != nil { - return errors.New("failed to create expected ip matcher").Base(err).AtWarning() + return newError("failed to create ip matcher").Base(err).AtWarning() } - expectedMatchers = append(expectedMatchers, matcher) - } - - // Establish unexpected IPs - var unexpectedMatchers []*router.GeoIPMatcher - for _, geoip := range ns.UnexpectedGeoip { - matcher, err := router.GlobalGeoIPContainer.Add(geoip) - if err != nil { - return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning() - } - unexpectedMatchers = append(unexpectedMatchers, matcher) + matchers = append(matchers, matcher) } if len(clientIP) > 0 { switch ns.Address.Address.GetAddress().(type) { case *net.IPOrDomain_Domain: - errors.LogInfo(ctx, "DNS: client ", ns.Address.Address.GetDomain(), " uses clientIP ", clientIP.String()) + newError("DNS: client ", ns.Address.Address.GetDomain(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog() case *net.IPOrDomain_Ip: - errors.LogInfo(ctx, "DNS: client ", net.IP(ns.Address.Address.GetIp()), " uses clientIP ", clientIP.String()) + newError("DNS: client ", ns.Address.Address.GetIp(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog() } } - var timeoutMs = 4000 * time.Millisecond - if ns.TimeoutMs > 0 { - timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond - } - - checkSystem := ns.QueryStrategy == QueryStrategy_USE_SYS - client.server = server + client.clientIP = clientIP client.skipFallback = ns.SkipFallback client.domains = rules - client.expectedIPs = expectedMatchers - client.unexpectedIPs = unexpectedMatchers - client.actPrior = ns.ActPrior - client.actUnprior = ns.ActUnprior - client.tag = tag - client.timeoutMs = timeoutMs - client.finalQuery = ns.FinalQuery - client.ipOption = &ipOption - client.checkSystem = checkSystem + client.expectIPs = matchers return nil }) return client, err } +// NewSimpleClient creates a DNS client with a simple destination. +func NewSimpleClient(ctx context.Context, endpoint *net.Endpoint, clientIP net.IP) (*Client, error) { + client := &Client{} + err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { + server, err := NewServer(endpoint.AsDestination(), dispatcher) + if err != nil { + return newError("failed to create nameserver").Base(err).AtWarning() + } + client.server = server + client.clientIP = clientIP + return nil + }) + + if len(clientIP) > 0 { + switch endpoint.Address.GetAddress().(type) { + case *net.IPOrDomain_Domain: + newError("DNS: client ", endpoint.Address.GetDomain(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog() + case *net.IPOrDomain_Ip: + newError("DNS: client ", endpoint.Address.GetIp(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog() + } + } + + return client, err +} + // Name returns the server name the client manages. func (c *Client) Name() string { 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. -func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) { - if c.checkSystem { - supportIPv4, supportIPv6 := checkSystemNetwork() - option.IPv4Enable = option.IPv4Enable && supportIPv4 - option.IPv6Enable = option.IPv6Enable && supportIPv6 - } else { - option.IPv4Enable = option.IPv4Enable && c.ipOption.IPv4Enable - option.IPv6Enable = option.IPv6Enable && c.ipOption.IPv6Enable - } - - if !option.IPv4Enable && !option.IPv6Enable { - return nil, 0, dns.ErrEmptyResponse - } - - ctx, cancel := context.WithTimeout(ctx, c.timeoutMs) - ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: c.tag}) - ips, ttl, err := c.server.QueryIP(ctx, domain, option) +func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, disableCache bool) ([]net.IP, error) { + ctx, cancel := context.WithTimeout(ctx, 4*time.Second) + ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, disableCache) cancel() if err != nil { - return nil, 0, err + return ips, err } - - if len(ips) == 0 { - return nil, 0, dns.ErrEmptyResponse - } - - if len(c.expectedIPs) > 0 && !c.actPrior { - ips = router.MatchIPs(c.expectedIPs, ips, false) - errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name()) - if len(ips) == 0 { - return nil, 0, dns.ErrEmptyResponse - } - } - - if len(c.unexpectedIPs) > 0 && !c.actUnprior { - ips = router.MatchIPs(c.unexpectedIPs, ips, true) - errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name()) - if len(ips) == 0 { - return nil, 0, dns.ErrEmptyResponse - } - } - - if len(c.expectedIPs) > 0 && c.actPrior { - ipsNew := router.MatchIPs(c.expectedIPs, ips, false) - if len(ipsNew) > 0 { - ips = ipsNew - errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name()) - } - } - - if len(c.unexpectedIPs) > 0 && c.actUnprior { - ipsNew := router.MatchIPs(c.unexpectedIPs, ips, true) - if len(ipsNew) > 0 { - ips = ipsNew - errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name()) - } - } - - return ips, ttl, nil + return c.MatchExpectedIPs(domain, ips) } -func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption) dns.IPOption { - switch queryStrategy { - case QueryStrategy_USE_IP: - return ipOption - case QueryStrategy_USE_SYS: - return ipOption - case QueryStrategy_USE_IP4: - return dns.IPOption{ - IPv4Enable: ipOption.IPv4Enable, - IPv6Enable: false, - FakeEnable: false, - } - case QueryStrategy_USE_IP6: - return dns.IPOption{ - IPv4Enable: false, - IPv6Enable: ipOption.IPv6Enable, - FakeEnable: false, - } - default: - return ipOption +// MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones. +func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) { + if len(c.expectIPs) == 0 { + return ips, nil } + newIps := []net.IP{} + for _, ip := range ips { + for _, matcher := range c.expectIPs { + if matcher.Match(ip) { + newIps = append(newIps, ip) + break + } + } + } + if len(newIps) == 0 { + return nil, errExpectedIPNonMatch + } + newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog() + return newIps, nil } diff --git a/app/dns/nameserver_doh.go b/app/dns/nameserver_doh.go index cba59423..fecc5efb 100644 --- a/app/dns/nameserver_doh.go +++ b/app/dns/nameserver_doh.go @@ -3,136 +3,234 @@ package dns import ( "bytes" "context" - "crypto/tls" - go_errors "errors" "fmt" "io" "net/http" "net/url" - "strings" + "sync" + "sync/atomic" "time" - utls "github.com/refraction-networking/utls" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/crypto" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/signal/pubsub" + "github.com/xtls/xray-core/common/task" dns_feature "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet" - "golang.org/x/net/http2" + "golang.org/x/net/dns/dnsmessage" ) // DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format, // which is compatible with traditional dns over udp(RFC1035), // thus most of the DOH implementation is copied from udpns.go type DoHNameServer struct { - cacheController *CacheController - httpClient *http.Client - dohURL string - clientIP net.IP + dispatcher routing.Dispatcher + sync.RWMutex + ips map[string]*record + pub *pubsub.Service + cleanup *task.Periodic + reqID uint32 + httpClient *http.Client + dohURL string + name string } -// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving. -func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, clientIP net.IP) *DoHNameServer { - url.Scheme = "https" - mode := "DOH" - if dispatcher == nil { - mode = "DOHL" - } - errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c) - s := &DoHNameServer{ - cacheController: NewCacheController(mode+"//"+url.Host, disableCache), - dohURL: url.String(), - clientIP: clientIP, +// NewDoHNameServer creates DOH server object for remote resolving. +func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServer, error) { + newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog() + s := baseDOHNameServer(url, "DOH") + + s.dispatcher = dispatcher + tr := &http.Transport{ + MaxIdleConns: 30, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 30 * time.Second, + ForceAttemptHTTP2: true, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + dest, err := net.ParseDestination(network + ":" + addr) + if err != nil { + return nil, err + } + link, err := s.dispatcher.Dispatch(toDnsContext(ctx, s.dohURL), dest) + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + + } + if err != nil { + return nil, err + } + + cc := common.ChainedClosable{} + if cw, ok := link.Writer.(common.Closable); ok { + cc = append(cc, cw) + } + if cr, ok := link.Reader.(common.Closable); ok { + cc = append(cc, cr) + } + return cnc.NewConnection( + cnc.ConnectionInputMulti(link.Writer), + cnc.ConnectionOutputMulti(link.Reader), + cnc.ConnectionOnClose(cc), + ), nil + }, } s.httpClient = &http.Client{ - Transport: &http2.Transport{ - IdleConnTimeout: net.ConnIdleTimeout, - ReadIdleTimeout: net.ChromeH2KeepAlivePeriod, - DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { - dest, err := net.ParseDestination(network + ":" + addr) - if err != nil { - return nil, err - } - var conn net.Conn - if dispatcher != nil { - dnsCtx := toDnsContext(ctx, s.dohURL) - if h2c { - dnsCtx = session.ContextWithMitmAlpn11(dnsCtx, false) // for insurance - dnsCtx = session.ContextWithMitmServerName(dnsCtx, url.Hostname()) - } - link, err := dispatcher.Dispatch(dnsCtx, dest) - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - if err != nil { - return nil, err - } - cc := common.ChainedClosable{} - if cw, ok := link.Writer.(common.Closable); ok { - cc = append(cc, cw) - } - if cr, ok := link.Reader.(common.Closable); ok { - cc = append(cc, cr) - } - conn = cnc.NewConnection( - cnc.ConnectionInputMulti(link.Writer), - cnc.ConnectionOutputMulti(link.Reader), - cnc.ConnectionOnClose(cc), - ) - } else { - log.Record(&log.AccessMessage{ - From: "DNS", - To: s.dohURL, - Status: log.AccessAccepted, - Detour: "local", - }) - conn, err = internet.DialSystem(ctx, dest, nil) - if err != nil { - return nil, err - } - } - if !h2c { - conn = utls.UClient(conn, &utls.Config{ServerName: url.Hostname()}, utls.HelloChrome_Auto) - if err := conn.(*utls.UConn).HandshakeContext(ctx); err != nil { - return nil, err - } - } - return conn, nil - }, + Timeout: time.Second * 180, + Transport: tr, + } + + return s, nil +} + +// NewDoHLocalNameServer creates DOH client object for local resolving +func NewDoHLocalNameServer(url *url.URL) *DoHNameServer { + url.Scheme = "https" + s := baseDOHNameServer(url, "DOHL") + tr := &http.Transport{ + IdleConnTimeout: 90 * time.Second, + ForceAttemptHTTP2: true, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + dest, err := net.ParseDestination(network + ":" + addr) + if err != nil { + return nil, err + } + conn, err := internet.DialSystem(ctx, dest, nil) + log.Record(&log.AccessMessage{ + From: "DNS", + To: s.dohURL, + Status: log.AccessAccepted, + Detour: "local", + }) + if err != nil { + return nil, err + } + return conn, nil }, } + s.httpClient = &http.Client{ + Timeout: time.Second * 180, + Transport: tr, + } + newError("DNS: created Local DOH client for ", url.String()).AtInfo().WriteToLog() + return s +} + +func baseDOHNameServer(url *url.URL, prefix string) *DoHNameServer { + s := &DoHNameServer{ + ips: make(map[string]*record), + pub: pubsub.NewService(), + name: prefix + "//" + url.Host, + dohURL: url.String(), + } + s.cleanup = &task.Periodic{ + Interval: time.Minute, + Execute: s.Cleanup, + } return s } // Name implements Server. func (s *DoHNameServer) Name() string { - return s.cacheController.name + return s.name +} + +// Cleanup clears expired items from cache +func (s *DoHNameServer) Cleanup() error { + now := time.Now() + s.Lock() + defer s.Unlock() + + if len(s.ips) == 0 { + return newError("nothing to do. stopping...") + } + + for domain, record := range s.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 { + newError(s.name, " cleanup ", domain).AtDebug().WriteToLog() + delete(s.ips, domain) + } else { + s.ips[domain] = record + } + } + + if len(s.ips) == 0 { + s.ips = make(map[string]*record) + } + + return nil +} + +func (s *DoHNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) { + elapsed := time.Since(req.start) + + s.Lock() + rec, found := s.ips[req.domain] + if !found { + rec = &record{} + } + updated := false + + switch req.reqType { + case dnsmessage.TypeA: + if isNewer(rec.A, ipRec) { + rec.A = ipRec + updated = true + } + case dnsmessage.TypeAAAA: + addr := make([]net.Address, 0, len(ipRec.IP)) + for _, ip := range ipRec.IP { + if len(ip.IP()) == net.IPv6len { + addr = append(addr, ip) + } + } + ipRec.IP = addr + if isNewer(rec.AAAA, ipRec) { + rec.AAAA = ipRec + updated = true + } + } + newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() + + if updated { + s.ips[req.domain] = rec + } + switch req.reqType { + case dnsmessage.TypeA: + s.pub.Publish(req.domain+"4", nil) + case dnsmessage.TypeAAAA: + s.pub.Publish(req.domain+"6", nil) + } + s.Unlock() + common.Must(s.cleanup.Start()) } func (s *DoHNameServer) newReqID() uint16 { - return 0 + return uint16(atomic.AddUint32(&s.reqID, 1)) } -func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) { - errors.LogInfo(ctx, s.Name(), " querying: ", domain) +func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { + newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx)) - if s.Name()+"." == "DOH//"+domain { - errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead.") - noResponseErrCh <- errors.New("tries to resolve itself!", s.Name()) + if s.name+"." == "DOH//"+domain { + newError(s.name, " tries to resolve itself! Use IP or set \"hosts\" instead.").AtError().WriteToLog(session.ExportIDToError(ctx)) 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 - // 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 := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300)))) + reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) var deadline time.Time if d, ok := ctx.Deadline(); ok { @@ -158,7 +256,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er }) // forced to use mux for DOH - // dnsCtx = session.ContextWithMuxPreferred(dnsCtx, true) + // dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true) var cancel context.CancelFunc dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline) @@ -166,23 +264,20 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er b, err := dns.PackMessage(r.msg) if err != nil { - errors.LogErrorInner(ctx, err, "failed to pack dns query for ", domain) - noResponseErrCh <- err + newError("failed to pack dns query for ", domain).Base(err).AtError().WriteToLog() return } resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes()) if err != nil { - errors.LogErrorInner(ctx, err, "failed to retrieve response for ", domain) - noResponseErrCh <- err + newError("failed to retrieve response for ", domain).Base(err).AtError().WriteToLog() return } rec, err := parseResponse(resp) if err != nil { - errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", domain) - noResponseErrCh <- err + newError("failed to handle DOH response for ", domain).Base(err).AtError().WriteToLog() return } - s.cacheController.updateIP(r, rec) + s.updateIP(r, rec) }(req) } } @@ -197,8 +292,6 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte, req.Header.Add("Accept", "application/dns-message") req.Header.Add("Content-Type", "application/dns-message") - req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000)))) - hc := s.httpClient resp, err := hc.Do(req.WithContext(ctx)) @@ -215,50 +308,103 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte, return io.ReadAll(resp.Body) } -// QueryIP implements Server. -func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { // nolint: dupl - fqdn := Fqdn(domain) - sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option) - defer closeSubscribers(sub4, sub6) +func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { + s.RLock() + record, found := s.ips[domain] + s.RUnlock() - if s.cacheController.disableCache { - errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name()) + if !found { + return nil, errRecordNotFound + } + + var err4 error + var err6 error + var ips []net.Address + var ip6 []net.Address + + if option.IPv4Enable { + ips, err4 = record.A.getIPs() + } + + if option.IPv6Enable { + ip6, err6 = record.AAAA.getIPs() + ips = append(ips, ip6...) + } + + if len(ips) > 0 { + return toNetIP(ips) + } + + if err4 != nil { + return nil, err4 + } + + if err6 != nil { + return nil, err6 + } + + if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) { + return nil, dns_feature.ErrEmptyResponse + } + + return nil, errRecordNotFound +} + +// QueryIP implements Server. +func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl + fqdn := Fqdn(domain) + + if disableCache { + newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } 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 + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) + return ips, err } } - noResponseErrCh := make(chan error, 2) - s.sendQuery(ctx, noResponseErrCh, fqdn, option) + // ipv4 and ipv6 belong to different subscription groups + var sub4, sub6 *pubsub.Subscriber + if option.IPv4Enable { + sub4 = s.pub.Subscribe(fqdn + "4") + defer sub4.Close() + } + if option.IPv6Enable { + sub6 = s.pub.Subscribe(fqdn + "6") + defer sub6.Close() + } + done := make(chan interface{}) + go func() { + if sub4 != nil { + select { + case <-sub4.Wait(): + case <-ctx.Done(): + } + } + if sub6 != nil { + select { + case <-sub6.Wait(): + case <-ctx.Done(): + } + } + close(done) + }() + s.sendQuery(ctx, fqdn, clientIP, option) start := time.Now() - if sub4 != nil { + for { + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) + return ips, err + } + select { case <-ctx.Done(): - return nil, 0, ctx.Err() - case err := <-noResponseErrCh: - return nil, 0, err - case <-sub4.Wait(): - sub4.Close() + return nil, ctx.Err() + case <-done: } } - 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 - } diff --git a/app/dns/nameserver_doh_test.go b/app/dns/nameserver_doh_test.go index 96412c22..f5cae5a6 100644 --- a/app/dns/nameserver_doh_test.go +++ b/app/dns/nameserver_doh_test.go @@ -17,12 +17,12 @@ func TestDOHNameServer(t *testing.T) { url, err := url.Parse("https+local://1.1.1.1/dns-query") common.Must(err) - s := NewDoHNameServer(url, nil, false, false, net.IP(nil)) + s := NewDoHLocalNameServer(url) 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", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, false) cancel() common.Must(err) if len(ips) == 0 { @@ -34,12 +34,12 @@ func TestDOHNameServerWithCache(t *testing.T) { url, err := url.Parse("https+local://1.1.1.1/dns-query") common.Must(err) - s := NewDoHNameServer(url, nil, false, false, net.IP(nil)) + s := NewDoHLocalNameServer(url) 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", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, false) cancel() common.Must(err) if len(ips) == 0 { @@ -47,59 +47,13 @@ func TestDOHNameServerWithCache(t *testing.T) { } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips2, _, err := s.QueryIP(ctx2, "google.com", dns_feature.IPOption{ + ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, true) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { t.Fatal(r) } } - -func TestDOHNameServerWithIPv4Override(t *testing.T) { - url, err := url.Parse("https+local://1.1.1.1/dns-query") - common.Must(err) - - s := NewDoHNameServer(url, nil, false, false, net.IP(nil)) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - }) - cancel() - common.Must(err) - if len(ips) == 0 { - t.Error("expect some ips, but got 0") - } - - for _, ip := range ips { - if len(ip) != net.IPv4len { - t.Error("expect only IPv4 response from DNS query") - } - } -} - -func TestDOHNameServerWithIPv6Override(t *testing.T) { - url, err := url.Parse("https+local://1.1.1.1/dns-query") - common.Must(err) - - s := NewDoHNameServer(url, nil, false, false, net.IP(nil)) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - }) - cancel() - common.Must(err) - if len(ips) == 0 { - t.Error("expect some ips, but got 0") - } - - for _, ip := range ips { - if len(ip) != net.IPv6len { - t.Error("expect only IPv6 response from DNS query") - } - } -} diff --git a/app/dns/nameserver_fakedns.go b/app/dns/nameserver_fakedns.go index 8c598ac8..44dcb01c 100644 --- a/app/dns/nameserver_fakedns.go +++ b/app/dns/nameserver_fakedns.go @@ -3,8 +3,8 @@ package dns import ( "context" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/dns" ) @@ -12,19 +12,22 @@ type FakeDNSServer struct { fakeDNSEngine dns.FakeDNSEngine } -func NewFakeDNSServer(fd dns.FakeDNSEngine) *FakeDNSServer { - return &FakeDNSServer{fakeDNSEngine: fd} +func NewFakeDNSServer() *FakeDNSServer { + return &FakeDNSServer{} } func (FakeDNSServer) Name() string { return "FakeDNS" } -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, _ net.IP, opt dns.IPOption, _ bool) ([]net.IP, error) { if f.fakeDNSEngine == nil { - return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError() + if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) { + f.fakeDNSEngine = fd + }); err != nil { + return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError() + } } - var ips []net.Address if fkr0, ok := f.fakeDNSEngine.(dns.FakeDNSEngineRev0); ok { ips = fkr0.GetFakeIPForDomain3(domain, opt.IPv4Enable, opt.IPv6Enable) @@ -34,13 +37,13 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOp netIP, err := toNetIP(ips) if err != nil { - return nil, 0, errors.New("Unable to convert IP to net ip").Base(err).AtError() + return nil, newError("Unable to convert IP to net ip").Base(err).AtError() } - errors.LogInfo(ctx, f.Name(), " got answer: ", domain, " -> ", ips) + newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog() if len(netIP) > 0 { - return netIP, 1, nil // fakeIP ttl is 1 + return netIP, nil } - return nil, 0, dns.ErrEmptyResponse + return nil, dns.ErrEmptyResponse } diff --git a/app/dns/nameserver_local.go b/app/dns/nameserver_local.go index 91b003e3..bf741c23 100644 --- a/app/dns/nameserver_local.go +++ b/app/dns/nameserver_local.go @@ -2,9 +2,9 @@ package dns import ( "context" + "strings" "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/features/dns" @@ -16,14 +16,19 @@ type LocalNameServer struct { client *localdns.Client } -// QueryIP implements Server. -func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) { +const errEmptyResponse = "No address associated with hostname" +// QueryIP implements Server. +func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) { start := time.Now() - ips, ttl, err = s.client.LookupIP(domain, option) + ips, err = s.client.LookupIP(domain, option) + + if err != nil && strings.HasSuffix(err.Error(), errEmptyResponse) { + err = dns.ErrEmptyResponse + } if len(ips) > 0 { - errors.LogInfo(ctx, "Localhost got answer: ", domain, " -> ", ips) + newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog() log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) } @@ -37,13 +42,13 @@ func (s *LocalNameServer) Name() string { // NewLocalNameServer creates localdns server object for directly lookup in system DNS. func NewLocalNameServer() *LocalNameServer { - errors.LogInfo(context.Background(), "DNS: created localhost client") + newError("DNS: created localhost client").AtInfo().WriteToLog() return &LocalNameServer{ client: localdns.New(), } } // NewLocalDNSClient creates localdns client object for directly lookup in system DNS. -func NewLocalDNSClient(ipOption dns.IPOption) *Client { - return &Client{server: NewLocalNameServer(), ipOption: &ipOption} +func NewLocalDNSClient() *Client { + return &Client{server: NewLocalNameServer()} } diff --git a/app/dns/nameserver_local_test.go b/app/dns/nameserver_local_test.go index 71aa08c4..3331a333 100644 --- a/app/dns/nameserver_local_test.go +++ b/app/dns/nameserver_local_test.go @@ -7,17 +7,18 @@ import ( . "github.com/xtls/xray-core/app/dns" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/dns" ) func TestLocalNameServer(t *testing.T) { s := NewLocalNameServer() 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", net.IP{}, dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, - }) + }, false) cancel() common.Must(err) if len(ips) == 0 { diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go index 5512edc4..a362ec84 100644 --- a/app/dns/nameserver_quic.go +++ b/app/dns/nameserver_quic.go @@ -1,47 +1,51 @@ package dns import ( - "bytes" "context" - "encoding/binary" - go_errors "errors" "net/url" "sync" + "sync/atomic" "time" "github.com/quic-go/quic-go" + "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/signal/pubsub" + "github.com/xtls/xray-core/common/task" dns_feature "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/transport/internet/tls" + "golang.org/x/net/dns/dnsmessage" "golang.org/x/net/http2" ) // NextProtoDQ - During connection establishment, DNS/QUIC support is indicated // by selecting the ALPN token "dq" in the crypto handshake. -const NextProtoDQ = "doq" +const NextProtoDQ = "doq-i00" const handshakeTimeout = time.Second * 8 // QUICNameServer implemented DNS over QUIC type QUICNameServer struct { sync.RWMutex - cacheController *CacheController - destination *net.Destination - connection quic.Connection - clientIP net.IP + ips map[string]*record + pub *pubsub.Service + cleanup *task.Periodic + reqID uint32 + name string + destination *net.Destination + connection quic.Connection } // NewQUICNameServer creates DNS-over-QUIC client object for local resolving -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()) +func NewQUICNameServer(url *url.URL) (*QUICNameServer, error) { + newError("DNS: created Local DNS-over-QUIC client for ", url.String()).AtInfo().WriteToLog() var err error - port := net.Port(853) + port := net.Port(784) if url.Port() != "" { port, err = net.PortFromString(url.Port()) if err != nil { @@ -51,9 +55,14 @@ func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICN dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port) s := &QUICNameServer{ - cacheController: NewCacheController(url.String(), disableCache), - destination: &dest, - clientIP: clientIP, + ips: make(map[string]*record), + pub: pubsub.NewService(), + name: url.String(), + destination: &dest, + } + s.cleanup = &task.Periodic{ + Interval: time.Minute, + Execute: s.Cleanup, } return s, nil @@ -61,17 +70,94 @@ func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICN // Name returns client name func (s *QUICNameServer) Name() string { - return s.cacheController.name + return s.name +} + +// Cleanup clears expired items from cache +func (s *QUICNameServer) Cleanup() error { + now := time.Now() + s.Lock() + defer s.Unlock() + + if len(s.ips) == 0 { + return newError("nothing to do. stopping...") + } + + for domain, record := range s.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 { + newError(s.name, " cleanup ", domain).AtDebug().WriteToLog() + delete(s.ips, domain) + } else { + s.ips[domain] = record + } + } + + if len(s.ips) == 0 { + s.ips = make(map[string]*record) + } + + return nil +} + +func (s *QUICNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) { + elapsed := time.Since(req.start) + + s.Lock() + rec, found := s.ips[req.domain] + if !found { + rec = &record{} + } + updated := false + + switch req.reqType { + case dnsmessage.TypeA: + if isNewer(rec.A, ipRec) { + rec.A = ipRec + updated = true + } + case dnsmessage.TypeAAAA: + addr := make([]net.Address, 0) + for _, ip := range ipRec.IP { + if len(ip.IP()) == net.IPv6len { + addr = append(addr, ip) + } + } + ipRec.IP = addr + if isNewer(rec.AAAA, ipRec) { + rec.AAAA = ipRec + updated = true + } + } + newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() + + if updated { + s.ips[req.domain] = rec + } + switch req.reqType { + case dnsmessage.TypeA: + s.pub.Publish(req.domain+"4", nil) + case dnsmessage.TypeAAAA: + s.pub.Publish(req.domain+"6", nil) + } + s.Unlock() + common.Must(s.cleanup.Start()) } func (s *QUICNameServer) newReqID() uint16 { - return 0 + return uint16(atomic.AddUint32(&s.reqID, 1)) } -func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) { - errors.LogInfo(ctx, s.Name(), " querying: ", domain) +func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { + newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx)) - reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0)) + reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) var deadline time.Time if d, ok := ctx.Deadline(); ok { @@ -102,37 +188,19 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e b, err := dns.PackMessage(r.msg) if err != nil { - errors.LogErrorInner(ctx, err, "failed to pack dns query") - noResponseErrCh <- err + newError("failed to pack dns query").Base(err).AtError().WriteToLog() return } - dnsReqBuf := buf.New() - err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) - if err != nil { - errors.LogErrorInner(ctx, err, "binary write failed") - noResponseErrCh <- err - return - } - _, err = dnsReqBuf.Write(b.Bytes()) - if err != nil { - errors.LogErrorInner(ctx, err, "buffer write failed") - noResponseErrCh <- err - return - } - b.Release() - conn, err := s.openStream(dnsCtx) if err != nil { - errors.LogErrorInner(ctx, err, "failed to open quic connection") - noResponseErrCh <- err + newError("failed to open quic connection").Base(err).AtError().WriteToLog() return } - _, err = conn.Write(dnsReqBuf.Bytes()) + _, err = conn.Write(b.Bytes()) if err != nil { - errors.LogErrorInner(ctx, err, "failed to send query") - noResponseErrCh <- err + newError("failed to send query").Base(err).AtError().WriteToLog() return } @@ -140,84 +208,121 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e respBuf := buf.New() defer respBuf.Release() - n, err := respBuf.ReadFullFrom(conn, 2) + n, err := respBuf.ReadFrom(conn) if err != nil && n == 0 { - errors.LogErrorInner(ctx, err, "failed to read response length") - noResponseErrCh <- err - return - } - var length int16 - err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) - if err != nil { - errors.LogErrorInner(ctx, err, "failed to parse response length") - noResponseErrCh <- err - return - } - respBuf.Clear() - n, err = respBuf.ReadFullFrom(conn, int32(length)) - if err != nil && n == 0 { - errors.LogErrorInner(ctx, err, "failed to read response length") - noResponseErrCh <- err + newError("failed to read response").Base(err).AtError().WriteToLog() return } rec, err := parseResponse(respBuf.Bytes()) if err != nil { - errors.LogErrorInner(ctx, err, "failed to handle response") - noResponseErrCh <- err + newError("failed to handle response").Base(err).AtError().WriteToLog() return } - s.cacheController.updateIP(r, rec) + s.updateIP(r, rec) }(req) } } -// QueryIP is called from dns.Server->queryIPTimeout -func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { - fqdn := Fqdn(domain) - sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option) - defer closeSubscribers(sub4, sub6) +func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { + s.RLock() + record, found := s.ips[domain] + s.RUnlock() - if s.cacheController.disableCache { - errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name()) + if !found { + return nil, errRecordNotFound + } + + var err4 error + var err6 error + var ips []net.Address + var ip6 []net.Address + + if option.IPv4Enable { + ips, err4 = record.A.getIPs() + } + + if option.IPv6Enable { + ip6, err6 = record.AAAA.getIPs() + ips = append(ips, ip6...) + } + + if len(ips) > 0 { + return toNetIP(ips) + } + + if err4 != nil { + return nil, err4 + } + + if err6 != nil { + return nil, err6 + } + + if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) { + return nil, dns_feature.ErrEmptyResponse + } + + return nil, errRecordNotFound +} + +// QueryIP is called from dns.Server->queryIPTimeout +func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { + fqdn := Fqdn(domain) + + if disableCache { + newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } 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 + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) + return ips, err } } - noResponseErrCh := make(chan error, 2) - s.sendQuery(ctx, noResponseErrCh, fqdn, option) + // ipv4 and ipv6 belong to different subscription groups + var sub4, sub6 *pubsub.Subscriber + if option.IPv4Enable { + sub4 = s.pub.Subscribe(fqdn + "4") + defer sub4.Close() + } + if option.IPv6Enable { + sub6 = s.pub.Subscribe(fqdn + "6") + defer sub6.Close() + } + done := make(chan interface{}) + go func() { + if sub4 != nil { + select { + case <-sub4.Wait(): + case <-ctx.Done(): + } + } + if sub6 != nil { + select { + case <-sub6.Wait(): + case <-ctx.Done(): + } + } + close(done) + }() + s.sendQuery(ctx, fqdn, clientIP, option) start := time.Now() - if sub4 != nil { + for { + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) + return ips, err + } + select { case <-ctx.Done(): - return nil, 0, ctx.Err() - case err := <-noResponseErrCh: - return nil, 0, err - case <-sub4.Wait(): - sub4.Close() + return nil, ctx.Err() + case <-done: } } - 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.Connection) bool { @@ -268,8 +373,8 @@ func (s *QUICNameServer) openConnection() (quic.Connection, error) { quicConfig := &quic.Config{ HandshakeIdleTimeout: handshakeTimeout, } - tlsConfig.ServerName = s.destination.Address.String() - conn, err := quic.DialAddr(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig) + + conn, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig) log.Record(&log.AccessMessage{ From: "DNS", To: s.destination, diff --git a/app/dns/nameserver_quic_test.go b/app/dns/nameserver_quic_test.go index fd11d2e6..cf445b43 100644 --- a/app/dns/nameserver_quic_test.go +++ b/app/dns/nameserver_quic_test.go @@ -14,74 +14,29 @@ import ( ) func TestQUICNameServer(t *testing.T) { - url, err := url.Parse("quic://dns.adguard-dns.com") + url, err := url.Parse("quic://dns.adguard.com") common.Must(err) - s, err := NewQUICNameServer(url, false, net.IP(nil)) + s, err := NewQUICNameServer(url) common.Must(err) 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", net.IP(nil), dns.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } + ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips2, _, err := s.QueryIP(ctx2, "google.com", dns.IPOption{ + ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, true) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { t.Fatal(r) } } - -func TestQUICNameServerWithIPv4Override(t *testing.T) { - url, err := url.Parse("quic://dns.adguard-dns.com") - common.Must(err) - s, err := NewQUICNameServer(url, false, net.IP(nil)) - common.Must(err) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - }) - cancel() - common.Must(err) - if len(ips) == 0 { - t.Error("expect some ips, but got 0") - } - - for _, ip := range ips { - if len(ip) != net.IPv4len { - t.Error("expect only IPv4 response from DNS query") - } - } -} - -func TestQUICNameServerWithIPv6Override(t *testing.T) { - url, err := url.Parse("quic://dns.adguard-dns.com") - common.Must(err) - s, err := NewQUICNameServer(url, false, net.IP(nil)) - common.Must(err) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - }) - cancel() - common.Must(err) - if len(ips) == 0 { - t.Error("expect some ips, but got 0") - } - - for _, ip := range ips { - if len(ip) != net.IPv6len { - t.Error("expect only IPv6 response from DNS query") - } - } -} diff --git a/app/dns/nameserver_tcp.go b/app/dns/nameserver_tcp.go index 1937c25c..cf63ac21 100644 --- a/app/dns/nameserver_tcp.go +++ b/app/dns/nameserver_tcp.go @@ -4,40 +4,41 @@ import ( "bytes" "context" "encoding/binary" - go_errors "errors" "net/url" + "sync" "sync/atomic" "time" + "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/signal/pubsub" + "github.com/xtls/xray-core/common/task" dns_feature "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet" + "golang.org/x/net/dns/dnsmessage" ) // TCPNameServer implemented DNS over TCP (RFC7766). type TCPNameServer struct { - cacheController *CacheController - destination *net.Destination - reqID uint32 - dial func(context.Context) (net.Conn, error) - clientIP net.IP + sync.RWMutex + name string + destination *net.Destination + ips map[string]*record + pub *pubsub.Service + cleanup *task.Periodic + reqID uint32 + dial func(context.Context) (net.Conn, error) } // NewTCPNameServer creates DNS over TCP server object for remote resolving. -func NewTCPNameServer( - url *url.URL, - dispatcher routing.Dispatcher, - disableCache bool, - clientIP net.IP, -) (*TCPNameServer, error) { - s, err := baseTCPNameServer(url, "TCP", disableCache, clientIP) +func NewTCPNameServer(url *url.URL, dispatcher routing.Dispatcher) (*TCPNameServer, error) { + s, err := baseTCPNameServer(url, "TCP") if err != nil { return nil, err } @@ -58,8 +59,8 @@ func NewTCPNameServer( } // NewTCPLocalNameServer creates DNS over TCP client object for local resolving -func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*TCPNameServer, error) { - s, err := baseTCPNameServer(url, "TCPL", disableCache, clientIP) +func NewTCPLocalNameServer(url *url.URL) (*TCPNameServer, error) { + s, err := baseTCPNameServer(url, "TCPL") if err != nil { return nil, err } @@ -71,20 +72,26 @@ func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*T return s, nil } -func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP net.IP) (*TCPNameServer, error) { +func baseTCPNameServer(url *url.URL, prefix string) (*TCPNameServer, error) { + var err error port := net.Port(53) if url.Port() != "" { - var err error - if port, err = net.PortFromString(url.Port()); err != nil { + port, err = net.PortFromString(url.Port()) + if err != nil { return nil, err } } dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port) s := &TCPNameServer{ - cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache), - destination: &dest, - clientIP: clientIP, + destination: &dest, + ips: make(map[string]*record), + pub: pubsub.NewService(), + name: prefix + "//" + dest.NetAddr(), + } + s.cleanup = &task.Periodic{ + Interval: time.Minute, + Execute: s.Cleanup, } return s, nil @@ -92,17 +99,94 @@ func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP // Name implements Server. func (s *TCPNameServer) Name() string { - return s.cacheController.name + return s.name +} + +// Cleanup clears expired items from cache +func (s *TCPNameServer) Cleanup() error { + now := time.Now() + s.Lock() + defer s.Unlock() + + if len(s.ips) == 0 { + return newError("nothing to do. stopping...") + } + + for domain, record := range s.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 { + newError(s.name, " cleanup ", domain).AtDebug().WriteToLog() + delete(s.ips, domain) + } else { + s.ips[domain] = record + } + } + + if len(s.ips) == 0 { + s.ips = make(map[string]*record) + } + + return nil +} + +func (s *TCPNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) { + elapsed := time.Since(req.start) + + s.Lock() + rec, found := s.ips[req.domain] + if !found { + rec = &record{} + } + updated := false + + switch req.reqType { + case dnsmessage.TypeA: + if isNewer(rec.A, ipRec) { + rec.A = ipRec + updated = true + } + case dnsmessage.TypeAAAA: + addr := make([]net.Address, 0) + for _, ip := range ipRec.IP { + if len(ip.IP()) == net.IPv6len { + addr = append(addr, ip) + } + } + ipRec.IP = addr + if isNewer(rec.AAAA, ipRec) { + rec.AAAA = ipRec + updated = true + } + } + newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() + + if updated { + s.ips[req.domain] = rec + } + switch req.reqType { + case dnsmessage.TypeA: + s.pub.Publish(req.domain+"4", nil) + case dnsmessage.TypeAAAA: + s.pub.Publish(req.domain+"6", nil) + } + s.Unlock() + common.Must(s.cleanup.Start()) } func (s *TCPNameServer) newReqID() uint16 { return uint16(atomic.AddUint32(&s.reqID, 1)) } -func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) { - errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain) +func (s *TCPNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { + newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx)) - reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0)) + reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) var deadline time.Time if d, ok := ctx.Deadline(); ok { @@ -130,37 +214,24 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er b, err := dns.PackMessage(r.msg) if err != nil { - errors.LogErrorInner(ctx, err, "failed to pack dns query") - noResponseErrCh <- err + newError("failed to pack dns query").Base(err).AtError().WriteToLog() return } conn, err := s.dial(dnsCtx) if err != nil { - errors.LogErrorInner(ctx, err, "failed to dial namesever") - noResponseErrCh <- err + newError("failed to dial namesever").Base(err).AtError().WriteToLog() return } defer conn.Close() dnsReqBuf := buf.New() - err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) - if err != nil { - errors.LogErrorInner(ctx, err, "binary write failed") - noResponseErrCh <- err - return - } - _, err = dnsReqBuf.Write(b.Bytes()) - if err != nil { - errors.LogErrorInner(ctx, err, "buffer write failed") - noResponseErrCh <- err - return - } + binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) + dnsReqBuf.Write(b.Bytes()) b.Release() _, err = conn.Write(dnsReqBuf.Bytes()) if err != nil { - errors.LogErrorInner(ctx, err, "failed to send query") - noResponseErrCh <- err + newError("failed to send query").Base(err).AtError().WriteToLog() return } dnsReqBuf.Release() @@ -169,81 +240,126 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er defer respBuf.Release() n, err := respBuf.ReadFullFrom(conn, 2) if err != nil && n == 0 { - errors.LogErrorInner(ctx, err, "failed to read response length") - noResponseErrCh <- err + newError("failed to read response length").Base(err).AtError().WriteToLog() return } var length int16 err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) if err != nil { - errors.LogErrorInner(ctx, err, "failed to parse response length") - noResponseErrCh <- err + newError("failed to parse response length").Base(err).AtError().WriteToLog() return } respBuf.Clear() n, err = respBuf.ReadFullFrom(conn, int32(length)) if err != nil && n == 0 { - errors.LogErrorInner(ctx, err, "failed to read response length") - noResponseErrCh <- err + newError("failed to read response length").Base(err).AtError().WriteToLog() return } rec, err := parseResponse(respBuf.Bytes()) if err != nil { - errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response") - noResponseErrCh <- err + newError("failed to parse DNS over TCP response").Base(err).AtError().WriteToLog() return } - s.cacheController.updateIP(r, rec) + s.updateIP(r, rec) }(req) } } -// QueryIP implements Server. -func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { - fqdn := Fqdn(domain) - sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option) - defer closeSubscribers(sub4, sub6) +func (s *TCPNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { + s.RLock() + record, found := s.ips[domain] + s.RUnlock() - if s.cacheController.disableCache { - errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name()) + if !found { + return nil, errRecordNotFound + } + + var err4 error + var err6 error + var ips []net.Address + var ip6 []net.Address + + if option.IPv4Enable { + ips, err4 = record.A.getIPs() + } + + if option.IPv6Enable { + ip6, err6 = record.AAAA.getIPs() + ips = append(ips, ip6...) + } + + if len(ips) > 0 { + return toNetIP(ips) + } + + if err4 != nil { + return nil, err4 + } + + if err6 != nil { + return nil, err6 + } + + return nil, dns_feature.ErrEmptyResponse +} + +// QueryIP implements Server. +func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { + fqdn := Fqdn(domain) + + if disableCache { + newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } 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 + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) + return ips, err } } - noResponseErrCh := make(chan error, 2) - s.sendQuery(ctx, noResponseErrCh, fqdn, option) + // ipv4 and ipv6 belong to different subscription groups + var sub4, sub6 *pubsub.Subscriber + if option.IPv4Enable { + sub4 = s.pub.Subscribe(fqdn + "4") + defer sub4.Close() + } + if option.IPv6Enable { + sub6 = s.pub.Subscribe(fqdn + "6") + defer sub6.Close() + } + done := make(chan interface{}) + go func() { + if sub4 != nil { + select { + case <-sub4.Wait(): + case <-ctx.Done(): + } + } + if sub6 != nil { + select { + case <-sub6.Wait(): + case <-ctx.Done(): + } + } + close(done) + }() + s.sendQuery(ctx, fqdn, clientIP, option) start := time.Now() - if sub4 != nil { + for { + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) + return ips, err + } + select { case <-ctx.Done(): - return nil, 0, ctx.Err() - case err := <-noResponseErrCh: - return nil, 0, err - case <-sub4.Wait(): - sub4.Close() + return nil, ctx.Err() + case <-done: } } - 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 - } diff --git a/app/dns/nameserver_tcp_test.go b/app/dns/nameserver_tcp_test.go index 074896ca..da362355 100644 --- a/app/dns/nameserver_tcp_test.go +++ b/app/dns/nameserver_tcp_test.go @@ -16,13 +16,13 @@ import ( func TestTCPLocalNameServer(t *testing.T) { url, err := url.Parse("tcp+local://8.8.8.8") common.Must(err) - s, err := NewTCPLocalNameServer(url, false, net.IP(nil)) + s, err := NewTCPLocalNameServer(url) common.Must(err) 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", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, false) cancel() common.Must(err) if len(ips) == 0 { @@ -33,13 +33,13 @@ func TestTCPLocalNameServer(t *testing.T) { func TestTCPLocalNameServerWithCache(t *testing.T) { url, err := url.Parse("tcp+local://8.8.8.8") common.Must(err) - s, err := NewTCPLocalNameServer(url, false, net.IP(nil)) + s, err := NewTCPLocalNameServer(url) common.Must(err) 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", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, false) cancel() common.Must(err) if len(ips) == 0 { @@ -47,61 +47,13 @@ func TestTCPLocalNameServerWithCache(t *testing.T) { } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips2, _, err := s.QueryIP(ctx2, "google.com", dns_feature.IPOption{ + ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }) + }, true) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { t.Fatal(r) } } - -func TestTCPLocalNameServerWithIPv4Override(t *testing.T) { - url, err := url.Parse("tcp+local://8.8.8.8") - common.Must(err) - s, err := NewTCPLocalNameServer(url, false, net.IP(nil)) - common.Must(err) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - }) - cancel() - common.Must(err) - - if len(ips) == 0 { - t.Error("expect some ips, but got 0") - } - - for _, ip := range ips { - if len(ip) != net.IPv4len { - t.Error("expect only IPv4 response from DNS query") - } - } -} - -func TestTCPLocalNameServerWithIPv6Override(t *testing.T) { - url, err := url.Parse("tcp+local://8.8.8.8") - common.Must(err) - s, err := NewTCPLocalNameServer(url, false, net.IP(nil)) - common.Must(err) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - }) - cancel() - common.Must(err) - - if len(ips) == 0 { - t.Error("expect some ips, but got 0") - } - - for _, ip := range ips { - if len(ip) != net.IPv6len { - t.Error("expect only IPv6 response from DNS query") - } - } -} diff --git a/app/dns/nameserver_udp.go b/app/dns/nameserver_udp.go index 3c25e612..d511df5b 100644 --- a/app/dns/nameserver_udp.go +++ b/app/dns/nameserver_udp.go @@ -2,18 +2,18 @@ package dns import ( "context" - go_errors "errors" "strings" "sync" "sync/atomic" "time" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol/dns" udp_proto "github.com/xtls/xray-core/common/protocol/udp" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/signal/pubsub" "github.com/xtls/xray-core/common/task" dns_feature "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/routing" @@ -24,55 +24,72 @@ import ( // ClassicNameServer implemented traditional UDP DNS. type ClassicNameServer struct { sync.RWMutex - cacheController *CacheController - address *net.Destination - requests map[uint16]*udpDnsRequest - udpServer *udp.Dispatcher - requestsCleanup *task.Periodic - reqID uint32 - clientIP net.IP -} - -type udpDnsRequest struct { - dnsRequest - ctx context.Context + name string + address *net.Destination + ips map[string]*record + requests map[uint16]*dnsRequest + pub *pubsub.Service + udpServer *udp.Dispatcher + cleanup *task.Periodic + reqID uint32 } // NewClassicNameServer creates udp server object for remote resolving. -func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) *ClassicNameServer { +func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher) *ClassicNameServer { // default to 53 if unspecific if address.Port == 0 { address.Port = net.Port(53) } s := &ClassicNameServer{ - cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache), - address: &address, - requests: make(map[uint16]*udpDnsRequest), - clientIP: clientIP, + address: &address, + ips: make(map[string]*record), + requests: make(map[uint16]*dnsRequest), + pub: pubsub.NewService(), + name: strings.ToUpper(address.String()), } - s.requestsCleanup = &task.Periodic{ + s.cleanup = &task.Periodic{ Interval: time.Minute, - Execute: s.RequestsCleanup, + Execute: s.Cleanup, } s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse) - errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr()) + newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog() return s } // Name implements Server. func (s *ClassicNameServer) Name() string { - return s.cacheController.name + return s.name } -// RequestsCleanup clears expired items from cache -func (s *ClassicNameServer) RequestsCleanup() error { +// Cleanup clears expired items from cache +func (s *ClassicNameServer) Cleanup() error { now := time.Now() s.Lock() defer s.Unlock() - if len(s.requests) == 0 { - return errors.New(s.Name(), " nothing to do. stopping...") + if len(s.ips) == 0 && len(s.requests) == 0 { + return newError(s.name, " nothing to do. stopping...") + } + + for domain, record := range s.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 { + newError(s.name, " cleanup ", domain).AtDebug().WriteToLog() + delete(s.ips, domain) + } else { + s.ips[domain] = record + } + } + + if len(s.ips) == 0 { + s.ips = make(map[string]*record) } for id, req := range s.requests { @@ -82,7 +99,7 @@ func (s *ClassicNameServer) RequestsCleanup() error { } if len(s.requests) == 0 { - s.requests = make(map[uint16]*udpDnsRequest) + s.requests = make(map[uint16]*dnsRequest) } return nil @@ -92,7 +109,7 @@ func (s *ClassicNameServer) RequestsCleanup() error { func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) { ipRec, err := parseResponse(packet.Payload.Bytes()) if err != nil { - errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp") + newError(s.name, " fail to parse responded DNS udp").AtError().WriteToLog() return } @@ -105,107 +122,175 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot } s.Unlock() if !ok { - errors.LogError(ctx, s.Name(), " cannot find the pending request") + newError(s.name, " cannot find the pending request").AtError().WriteToLog() return } - // if truncated, retry with EDNS0 option(udp payload size: 1350) - if ipRec.RawHeader.Truncated { - // if already has EDNS0 option, no need to retry - if len(req.msg.Additionals) == 0 { - // copy necessary meta data from original request - // and add EDNS0 option - opt := new(dnsmessage.Resource) - common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true)) - opt.Body = &dnsmessage.OPTResource{} - newMsg := *req.msg - newReq := *req - newMsg.Additionals = append(newMsg.Additionals, *opt) - newMsg.ID = s.newReqID() - newReq.msg = &newMsg - s.addPendingRequest(&newReq) - b, _ := dns.PackMessage(newReq.msg) - s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b) - return - } + var rec record + switch req.reqType { + case dnsmessage.TypeA: + rec.A = ipRec + case dnsmessage.TypeAAAA: + rec.AAAA = ipRec } - s.cacheController.updateIP(&req.dnsRequest, ipRec) + elapsed := time.Since(req.start) + newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() + if len(req.domain) > 0 && (rec.A != nil || rec.AAAA != nil) { + s.updateIP(req.domain, &rec) + } +} + +func (s *ClassicNameServer) updateIP(domain string, newRec *record) { + s.Lock() + + rec, found := s.ips[domain] + if !found { + rec = &record{} + } + + updated := false + if isNewer(rec.A, newRec.A) { + rec.A = newRec.A + updated = true + } + if isNewer(rec.AAAA, newRec.AAAA) { + rec.AAAA = newRec.AAAA + updated = true + } + + if updated { + newError(s.name, " updating IP records for domain:", domain).AtDebug().WriteToLog() + s.ips[domain] = rec + } + if newRec.A != nil { + s.pub.Publish(domain+"4", nil) + } + if newRec.AAAA != nil { + s.pub.Publish(domain+"6", nil) + } + s.Unlock() + common.Must(s.cleanup.Start()) } func (s *ClassicNameServer) newReqID() uint16 { return uint16(atomic.AddUint32(&s.reqID, 1)) } -func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) { +func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) { s.Lock() + defer s.Unlock() + id := req.msg.ID req.expire = time.Now().Add(time.Second * 8) s.requests[id] = req - s.Unlock() - common.Must(s.requestsCleanup.Start()) } -func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domain string, option dns_feature.IPOption) { - errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain) +func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { + newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx)) - reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0)) + reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) for _, req := range reqs { - udpReq := &udpDnsRequest{ - dnsRequest: *req, - ctx: ctx, - } - s.addPendingRequest(udpReq) + s.addPendingRequest(req) b, _ := dns.PackMessage(req.msg) s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b) } } -// QueryIP implements Server. -func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { - fqdn := Fqdn(domain) - sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option) - defer closeSubscribers(sub4, sub6) +func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { + s.RLock() + record, found := s.ips[domain] + s.RUnlock() - if s.cacheController.disableCache { - errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name()) + if !found { + return nil, errRecordNotFound + } + + var err4 error + var err6 error + var ips []net.Address + var ip6 []net.Address + + if option.IPv4Enable { + ips, err4 = record.A.getIPs() + } + + if option.IPv6Enable { + ip6, err6 = record.AAAA.getIPs() + ips = append(ips, ip6...) + } + + if len(ips) > 0 { + return toNetIP(ips) + } + + if err4 != nil { + return nil, err4 + } + + if err6 != nil { + return nil, err6 + } + + return nil, dns_feature.ErrEmptyResponse +} + +// QueryIP implements Server. +func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { + fqdn := Fqdn(domain) + + if disableCache { + newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } 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 + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) + return ips, err } } - noResponseErrCh := make(chan error, 2) - s.sendQuery(ctx, noResponseErrCh, fqdn, option) + // ipv4 and ipv6 belong to different subscription groups + var sub4, sub6 *pubsub.Subscriber + if option.IPv4Enable { + sub4 = s.pub.Subscribe(fqdn + "4") + defer sub4.Close() + } + if option.IPv6Enable { + sub6 = s.pub.Subscribe(fqdn + "6") + defer sub6.Close() + } + done := make(chan interface{}) + go func() { + if sub4 != nil { + select { + case <-sub4.Wait(): + case <-ctx.Done(): + } + } + if sub6 != nil { + select { + case <-sub6.Wait(): + case <-ctx.Done(): + } + } + close(done) + }() + s.sendQuery(ctx, fqdn, clientIP, option) start := time.Now() - if sub4 != nil { + for { + ips, err := s.findIPsForDomain(fqdn, option) + if err != errRecordNotFound { + log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) + return ips, err + } + select { case <-ctx.Done(): - return nil, 0, ctx.Err() - case err := <-noResponseErrCh: - return nil, 0, err - case <-sub4.Wait(): - sub4.Close() + return nil, ctx.Err() + case <-done: } } - 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 - } diff --git a/app/log/command/command.go b/app/log/command/command.go index 2df8ce6d..74a02d77 100644 --- a/app/log/command/command.go +++ b/app/log/command/command.go @@ -1,11 +1,12 @@ package command +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "github.com/xtls/xray-core/app/log" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/core" grpc "google.golang.org/grpc" ) @@ -18,13 +19,13 @@ type LoggerServer struct { func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) { logger := s.V.GetFeature((*log.Instance)(nil)) if logger == nil { - return nil, errors.New("unable to get logger instance") + return nil, newError("unable to get logger instance") } if err := logger.Close(); err != nil { - return nil, errors.New("failed to close logger").Base(err) + return nil, newError("failed to close logger").Base(err) } if err := logger.Start(); err != nil { - return nil, errors.New("failed to start logger").Base(err) + return nil, newError("failed to start logger").Base(err) } return &RestartLoggerResponse{}, nil } diff --git a/app/log/command/config.pb.go b/app/log/command/config.pb.go index 5cb5e43c..ceda9127 100644 --- a/app/log/command/config.pb.go +++ b/app/log/command/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/log/command/config.proto package command @@ -28,9 +28,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_log_command_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_log_command_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -41,7 +43,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -64,9 +66,11 @@ type RestartLoggerRequest struct { func (x *RestartLoggerRequest) Reset() { *x = RestartLoggerRequest{} - mi := &file_app_log_command_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_log_command_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RestartLoggerRequest) String() string { @@ -77,7 +81,7 @@ func (*RestartLoggerRequest) ProtoMessage() {} func (x *RestartLoggerRequest) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -100,9 +104,11 @@ type RestartLoggerResponse struct { func (x *RestartLoggerResponse) Reset() { *x = RestartLoggerResponse{} - mi := &file_app_log_command_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_log_command_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RestartLoggerResponse) String() string { @@ -113,7 +119,7 @@ func (*RestartLoggerResponse) ProtoMessage() {} func (x *RestartLoggerResponse) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -168,7 +174,7 @@ func file_app_log_command_config_proto_rawDescGZIP() []byte { } var file_app_log_command_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_app_log_command_config_proto_goTypes = []any{ +var file_app_log_command_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.app.log.command.Config (*RestartLoggerRequest)(nil), // 1: xray.app.log.command.RestartLoggerRequest (*RestartLoggerResponse)(nil), // 2: xray.app.log.command.RestartLoggerResponse @@ -188,6 +194,44 @@ func file_app_log_command_config_proto_init() { if File_app_log_command_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_log_command_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_log_command_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RestartLoggerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_log_command_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RestartLoggerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/log/command/config_grpc.pb.go b/app/log/command/config_grpc.pb.go index 492e998d..653c395c 100644 --- a/app/log/command/config_grpc.pb.go +++ b/app/log/command/config_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.2 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.12 // source: app/log/command/config.proto package command @@ -15,12 +15,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - LoggerService_RestartLogger_FullMethodName = "/xray.app.log.command.LoggerService/RestartLogger" -) +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 // LoggerServiceClient is the client API for LoggerService service. // @@ -38,9 +34,8 @@ func NewLoggerServiceClient(cc grpc.ClientConnInterface) LoggerServiceClient { } func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RestartLoggerResponse) - err := c.cc.Invoke(ctx, LoggerService_RestartLogger_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.log.command.LoggerService/RestartLogger", in, out, opts...) if err != nil { return nil, err } @@ -49,24 +44,20 @@ func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLogg // LoggerServiceServer is the server API for LoggerService service. // All implementations must embed UnimplementedLoggerServiceServer -// for forward compatibility. +// for forward compatibility type LoggerServiceServer interface { RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) mustEmbedUnimplementedLoggerServiceServer() } -// UnimplementedLoggerServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedLoggerServiceServer struct{} +// UnimplementedLoggerServiceServer must be embedded to have forward compatible implementations. +type UnimplementedLoggerServiceServer struct { +} func (UnimplementedLoggerServiceServer) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RestartLogger not implemented") } func (UnimplementedLoggerServiceServer) mustEmbedUnimplementedLoggerServiceServer() {} -func (UnimplementedLoggerServiceServer) testEmbeddedByValue() {} // UnsafeLoggerServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to LoggerServiceServer will @@ -76,13 +67,6 @@ type UnsafeLoggerServiceServer interface { } func RegisterLoggerServiceServer(s grpc.ServiceRegistrar, srv LoggerServiceServer) { - // If the following call pancis, it indicates UnimplementedLoggerServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&LoggerService_ServiceDesc, srv) } @@ -96,7 +80,7 @@ func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: LoggerService_RestartLogger_FullMethodName, + FullMethod: "/xray.app.log.command.LoggerService/RestartLogger", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest)) diff --git a/app/log/command/errors.generated.go b/app/log/command/errors.generated.go new file mode 100644 index 00000000..a1305932 --- /dev/null +++ b/app/log/command/errors.generated.go @@ -0,0 +1,9 @@ +package command + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/log/config.pb.go b/app/log/config.pb.go index 6a07685d..a70d2923 100644 --- a/app/log/config.pb.go +++ b/app/log/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/log/config.proto package log @@ -84,14 +84,15 @@ type Config struct { 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() { *x = Config{} - mi := &file_app_log_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_log_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -102,7 +103,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_log_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -159,20 +160,13 @@ func (x *Config) GetEnableDnsLog() bool { return false } -func (x *Config) GetMaskAddress() string { - if x != nil { - return x.MaskAddress - } - return "" -} - var File_app_log_config_proto protoreflect.FileDescriptor var file_app_log_config_proto_rawDesc = []byte{ 0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x1a, 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6c, 0x6f, 0x67, - 0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xde, 0x02, 0x0a, 0x06, 0x43, + 0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67, @@ -192,18 +186,15 @@ var file_app_log_config_proto_rawDesc = []byte{ 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, + 0x6c, 0x65, 0x44, 0x6e, 0x73, 0x4c, 0x6f, 0x67, 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 ( @@ -220,7 +211,7 @@ func file_app_log_config_proto_rawDescGZIP() []byte { var file_app_log_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_log_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_app_log_config_proto_goTypes = []any{ +var file_app_log_config_proto_goTypes = []interface{}{ (LogType)(0), // 0: xray.app.log.LogType (*Config)(nil), // 1: xray.app.log.Config (log.Severity)(0), // 2: xray.common.log.Severity @@ -241,6 +232,20 @@ func file_app_log_config_proto_init() { if File_app_log_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_log_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/log/config.proto b/app/log/config.proto index 8dc729d0..47e010d2 100644 --- a/app/log/config.proto +++ b/app/log/config.proto @@ -23,5 +23,4 @@ message Config { LogType access_log_type = 4; string access_log_path = 5; bool enable_dns_log = 6; - string mask_address= 7; } diff --git a/app/log/errors.generated.go b/app/log/errors.generated.go new file mode 100644 index 00000000..e09634a9 --- /dev/null +++ b/app/log/errors.generated.go @@ -0,0 +1,9 @@ +package log + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/log/log.go b/app/log/log.go index da69d3b6..7cdd5390 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -1,14 +1,12 @@ package log +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - "fmt" - "regexp" - "strings" "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" ) @@ -31,13 +29,13 @@ func New(ctx context.Context, config *Config) (*Instance, error) { } log.RegisterHandler(g) - // start logger now, - // then other modules will be able to log during initialization + // start logger instantly on inited + // other modules would log during init if err := g.startInternal(); err != nil { return nil, err } - errors.LogDebug(ctx, "Logger started") + newError("Logger started").AtDebug().WriteToLog() return g, nil } @@ -79,10 +77,10 @@ func (g *Instance) startInternal() error { g.active = true if err := g.initAccessLogger(); err != nil { - return errors.New("failed to initialize access logger").Base(err).AtWarning() + return newError("failed to initialize access logger").Base(err).AtWarning() } if err := g.initErrorLogger(); err != nil { - return errors.New("failed to initialize error logger").Base(err).AtWarning() + return newError("failed to initialize error logger").Base(err).AtWarning() } return nil @@ -102,25 +100,18 @@ func (g *Instance) Handle(msg log.Message) { return } - var Msg log.Message - if g.config.MaskAddress != "" { - Msg = &MaskedMsgWrapper{Message: msg, config: g.config} - } else { - Msg = msg - } - switch msg := msg.(type) { case *log.AccessMessage: if g.accessLogger != nil { - g.accessLogger.Handle(Msg) + g.accessLogger.Handle(msg) } case *log.DNSLog: if g.dns && g.accessLogger != nil { - g.accessLogger.Handle(Msg) + g.accessLogger.Handle(msg) } case *log.GeneralMessage: if g.errorLogger != nil && msg.Severity <= g.config.ErrorLogLevel { - g.errorLogger.Handle(Msg) + g.errorLogger.Handle(msg) } default: // Swallow @@ -129,7 +120,7 @@ func (g *Instance) Handle(msg log.Message) { // Close implements common.Closable.Close(). func (g *Instance) Close() error { - errors.LogDebug(context.Background(), "Logger closing") + newError("Logger closing").AtDebug().WriteToLog() g.Lock() defer g.Unlock() @@ -149,56 +140,6 @@ func (g *Instance) Close() error { return nil } -// MaskedMsgWrapper is to wrap the string() method to mask IP addresses in the log. -type MaskedMsgWrapper struct { - log.Message - config *Config -} - -func (m *MaskedMsgWrapper) String() string { - str := m.Message.String() - - 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})(?:[\/\\%](\d{1,3}))?`) - - // Process ipv4 - maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(ip string) string { - 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 - } - }) - - // process ipv6 - maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(ip string) string { - parts := strings.Split(ip, ":") - 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 - } - return ip - }) - - return maskedMsg -} - func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) diff --git a/app/log/log_creator.go b/app/log/log_creator.go index 48287271..9db8e1c7 100644 --- a/app/log/log_creator.go +++ b/app/log/log_creator.go @@ -4,7 +4,6 @@ import ( "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" ) @@ -20,7 +19,7 @@ var handlerCreatorMapLock = &sync.RWMutex{} func RegisterHandlerCreator(logType LogType, f HandlerCreator) error { if f == nil { - return errors.New("nil HandlerCreator") + return newError("nil HandlerCreator") } handlerCreatorMapLock.Lock() @@ -36,7 +35,7 @@ func createHandler(logType LogType, options HandlerCreatorOptions) (log.Handler, creator, found := handlerCreatorMap[logType] if !found { - return nil, errors.New("unable to create log handler for ", logType) + return nil, newError("unable to create log handler for ", logType) } return creator(logType, options) } diff --git a/app/metrics/config.pb.go b/app/metrics/config.pb.go index b0874b3b..24c5629e 100644 --- a/app/metrics/config.pb.go +++ b/app/metrics/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/metrics/config.proto package metrics @@ -27,15 +27,16 @@ type Config struct { 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"` + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_app_metrics_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_metrics_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -46,7 +47,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_metrics_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -68,28 +69,20 @@ func (x *Config) GetTag() string { return "" } -func (x *Config) GetListen() string { - if x != nil { - return x.Listen - } - return "" -} - var File_app_metrics_config_proto protoreflect.FileDescriptor var file_app_metrics_config_proto_rawDesc = []byte{ 0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x32, 0x0a, 0x06, + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x1a, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, - 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, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, + 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, + 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, + 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -105,7 +98,7 @@ func file_app_metrics_config_proto_rawDescGZIP() []byte { } var file_app_metrics_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_app_metrics_config_proto_goTypes = []any{ +var file_app_metrics_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.app.metrics.Config } var file_app_metrics_config_proto_depIdxs = []int32{ @@ -121,6 +114,20 @@ func file_app_metrics_config_proto_init() { if File_app_metrics_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_metrics_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/metrics/config.proto b/app/metrics/config.proto index 0441df03..5e6880f1 100644 --- a/app/metrics/config.proto +++ b/app/metrics/config.proto @@ -10,5 +10,4 @@ option java_multiple_files = true; message Config { // Tag of the outbound handler that handles metrics http connections. string tag = 1; - string listen = 2; } diff --git a/app/metrics/errors.generated.go b/app/metrics/errors.generated.go new file mode 100644 index 00000000..dee6b316 --- /dev/null +++ b/app/metrics/errors.generated.go @@ -0,0 +1,9 @@ +package metrics + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/metrics/metrics.go b/app/metrics/metrics.go index 1dc5b2f5..9b0ba071 100644 --- a/app/metrics/metrics.go +++ b/app/metrics/metrics.go @@ -10,7 +10,6 @@ import ( "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/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/core" @@ -24,15 +23,12 @@ type MetricsHandler struct { statsManager feature_stats.Manager observatory extension.Observatory tag string - listen string - tcpListener net.Listener } // NewMetricsHandler creates a new MetricsHandler based on the given config. func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) { c := &MetricsHandler{ - tag: config.Tag, - listen: config.Listen, + tag: config.Tag, } common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) { c.statsManager = sm @@ -90,23 +86,6 @@ func (p *MetricsHandler) Type() interface{} { } func (p *MetricsHandler) Start() error { - - // direct listen a port if listen is set - if p.listen != "" { - TCPlistener, err := net.Listen("tcp", p.listen) - if err != nil { - return err - } - p.tcpListener = TCPlistener - errors.LogInfo(context.Background(), "Metrics server listening on ", p.listen) - - go func() { - if err := http.Serve(TCPlistener, http.DefaultServeMux); err != nil { - errors.LogErrorInner(context.Background(), err, "failed to start metrics server") - } - }() - } - listener := &OutboundListener{ buffer: make(chan net.Conn, 4), done: done.New(), @@ -114,12 +93,12 @@ func (p *MetricsHandler) Start() error { go func() { if err := http.Serve(listener, http.DefaultServeMux); err != nil { - errors.LogErrorInner(context.Background(), err, "failed to start metrics server") + newError("failed to start metrics server").Base(err).AtError().WriteToLog() } }() if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil { - errors.LogInfo(context.Background(), "failed to remove existing handler") + newError("failed to remove existing handler").WriteToLog() } return p.ohm.AddHandler(context.Background(), &Outbound{ diff --git a/app/metrics/outbound.go b/app/metrics/outbound.go index e55b33fb..32c0192b 100644 --- a/app/metrics/outbound.go +++ b/app/metrics/outbound.go @@ -5,10 +5,8 @@ import ( "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" - "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/transport" ) @@ -33,7 +31,7 @@ func (l *OutboundListener) add(conn net.Conn) { func (l *OutboundListener) Accept() (net.Conn, error) { select { case <-l.done.Wait(): - return nil, errors.New("listen closed") + return nil, newError("listen closed") case c := <-l.buffer: return c, nil } @@ -109,13 +107,3 @@ func (co *Outbound) Close() error { co.closed = true return co.listener.Close() } - -// SenderSettings implements outbound.Handler. -func (co *Outbound) SenderSettings() *serial.TypedMessage { - return nil -} - -// ProxySettings implements outbound.Handler. -func (co *Outbound) ProxySettings() *serial.TypedMessage { - return nil -} diff --git a/app/observatory/burst/burst.go b/app/observatory/burst/burst.go deleted file mode 100644 index 8658086a..00000000 --- a/app/observatory/burst/burst.go +++ /dev/null @@ -1,12 +0,0 @@ -package burst - -import ( - "math" - "time" -) - -const ( - rttFailed = time.Duration(math.MaxInt64 - iota) - rttUntested - rttUnqualified -) diff --git a/app/observatory/burst/burstobserver.go b/app/observatory/burst/burstobserver.go deleted file mode 100644 index 472351cc..00000000 --- a/app/observatory/burst/burstobserver.go +++ /dev/null @@ -1,113 +0,0 @@ -package burst - -import ( - "context" - - "sync" - - "github.com/xtls/xray-core/app/observatory" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/signal/done" - "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/features/extension" - "github.com/xtls/xray-core/features/outbound" - "github.com/xtls/xray-core/features/routing" - "google.golang.org/protobuf/proto" -) - -type Observer struct { - config *Config - ctx context.Context - - statusLock sync.Mutex - hp *HealthPing - - finished *done.Instance - - ohm outbound.Manager -} - -func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) { - return &observatory.ObservationResult{Status: o.createResult()}, nil -} - -func (o *Observer) createResult() []*observatory.OutboundStatus { - var result []*observatory.OutboundStatus - o.hp.access.Lock() - defer o.hp.access.Unlock() - for name, value := range o.hp.Results { - status := observatory.OutboundStatus{ - Alive: value.getStatistics().All != value.getStatistics().Fail, - Delay: value.getStatistics().Average.Milliseconds(), - LastErrorReason: "", - OutboundTag: name, - LastSeenTime: 0, - LastTryTime: 0, - HealthPing: &observatory.HealthPingMeasurementResult{ - All: int64(value.getStatistics().All), - Fail: int64(value.getStatistics().Fail), - Deviation: int64(value.getStatistics().Deviation), - Average: int64(value.getStatistics().Average), - Max: int64(value.getStatistics().Max), - Min: int64(value.getStatistics().Min), - }, - } - result = append(result, &status) - } - return result -} - -func (o *Observer) Type() interface{} { - return extension.ObservatoryType() -} - -func (o *Observer) Start() error { - if o.config != nil && len(o.config.SubjectSelector) != 0 { - o.finished = done.New() - o.hp.StartScheduler(func() ([]string, error) { - hs, ok := o.ohm.(outbound.HandlerSelector) - if !ok { - - return nil, errors.New("outbound.Manager is not a HandlerSelector") - } - - outbounds := hs.Select(o.config.SubjectSelector) - return outbounds, nil - }) - } - return nil -} - -func (o *Observer) Close() error { - if o.finished != nil { - o.hp.StopScheduler() - return o.finished.Close() - } - return nil -} - -func New(ctx context.Context, config *Config) (*Observer, error) { - var outboundManager outbound.Manager - var dispatcher routing.Dispatcher - err := core.RequireFeatures(ctx, func(om outbound.Manager, rd routing.Dispatcher) { - outboundManager = om - dispatcher = rd - }) - if err != nil { - return nil, errors.New("Cannot get depended features").Base(err) - } - hp := NewHealthPing(ctx, dispatcher, config.PingConfig) - return &Observer{ - config: config, - ctx: ctx, - ohm: outboundManager, - hp: hp, - }, nil -} - -func init() { - common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - return New(ctx, config.(*Config)) - })) -} diff --git a/app/observatory/burst/config.pb.go b/app/observatory/burst/config.pb.go deleted file mode 100644 index 570384ae..00000000 --- a/app/observatory/burst/config.pb.go +++ /dev/null @@ -1,257 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: app/observatory/burst/config.proto - -package burst - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // @Document The selectors for outbound under observation - 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"` -} - -func (x *Config) Reset() { - *x = Config{} - mi := &file_app_observatory_burst_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Config) ProtoMessage() {} - -func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_app_observatory_burst_config_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Config.ProtoReflect.Descriptor instead. -func (*Config) Descriptor() ([]byte, []int) { - return file_app_observatory_burst_config_proto_rawDescGZIP(), []int{0} -} - -func (x *Config) GetSubjectSelector() []string { - if x != nil { - return x.SubjectSelector - } - return nil -} - -func (x *Config) GetPingConfig() *HealthPingConfig { - if x != nil { - return x.PingConfig - } - return nil -} - -type HealthPingConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // destination url, need 204 for success return - // default https://connectivitycheck.gstatic.com/generate_204 - Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` - // connectivity check url - Connectivity string `protobuf:"bytes,2,opt,name=connectivity,proto3" json:"connectivity,omitempty"` - // health check interval, int64 values of time.Duration - Interval int64 `protobuf:"varint,3,opt,name=interval,proto3" json:"interval,omitempty"` - // sampling count is the amount of recent ping results which are kept for calculation - SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"` - // ping timeout, int64 values of time.Duration - Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` - // http method to make request - HttpMethod string `protobuf:"bytes,6,opt,name=httpMethod,proto3" json:"httpMethod,omitempty"` -} - -func (x *HealthPingConfig) Reset() { - *x = HealthPingConfig{} - mi := &file_app_observatory_burst_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HealthPingConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthPingConfig) ProtoMessage() {} - -func (x *HealthPingConfig) ProtoReflect() protoreflect.Message { - mi := &file_app_observatory_burst_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 HealthPingConfig.ProtoReflect.Descriptor instead. -func (*HealthPingConfig) Descriptor() ([]byte, []int) { - return file_app_observatory_burst_config_proto_rawDescGZIP(), []int{1} -} - -func (x *HealthPingConfig) GetDestination() string { - if x != nil { - return x.Destination - } - return "" -} - -func (x *HealthPingConfig) GetConnectivity() string { - if x != nil { - return x.Connectivity - } - return "" -} - -func (x *HealthPingConfig) GetInterval() int64 { - if x != nil { - return x.Interval - } - return 0 -} - -func (x *HealthPingConfig) GetSamplingCount() int32 { - if x != nil { - return x.SamplingCount - } - return 0 -} - -func (x *HealthPingConfig) GetTimeout() int64 { - if x != nil { - return x.Timeout - } - return 0 -} - -func (x *HealthPingConfig) GetHttpMethod() string { - if x != nil { - return x.HttpMethod - } - return "" -} - -var File_app_observatory_burst_config_proto protoreflect.FileDescriptor - -var file_app_observatory_burst_config_proto_rawDesc = []byte{ - 0x0a, 0x22, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, - 0x79, 0x2f, 0x62, 0x75, 0x72, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 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, - 0x62, 0x75, 0x72, 0x73, 0x74, 0x22, 0x87, 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, 0x52, 0x0a, 0x0b, 0x70, - 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x31, 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, 0x62, 0x75, 0x72, - 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, - 0xd4, 0x01, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 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 ( - file_app_observatory_burst_config_proto_rawDescOnce sync.Once - file_app_observatory_burst_config_proto_rawDescData = file_app_observatory_burst_config_proto_rawDesc -) - -func file_app_observatory_burst_config_proto_rawDescGZIP() []byte { - file_app_observatory_burst_config_proto_rawDescOnce.Do(func() { - 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 -} - -var file_app_observatory_burst_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_app_observatory_burst_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.core.app.observatory.burst.Config - (*HealthPingConfig)(nil), // 1: xray.core.app.observatory.burst.HealthPingConfig -} -var file_app_observatory_burst_config_proto_depIdxs = []int32{ - 1, // 0: xray.core.app.observatory.burst.Config.ping_config:type_name -> xray.core.app.observatory.burst.HealthPingConfig - 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_observatory_burst_config_proto_init() } -func file_app_observatory_burst_config_proto_init() { - if File_app_observatory_burst_config_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_app_observatory_burst_config_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_app_observatory_burst_config_proto_goTypes, - DependencyIndexes: file_app_observatory_burst_config_proto_depIdxs, - MessageInfos: file_app_observatory_burst_config_proto_msgTypes, - }.Build() - 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_depIdxs = nil -} diff --git a/app/observatory/burst/config.proto b/app/observatory/burst/config.proto deleted file mode 100644 index a60978d5..00000000 --- a/app/observatory/burst/config.proto +++ /dev/null @@ -1,32 +0,0 @@ -syntax = "proto3"; - -package xray.core.app.observatory.burst; -option csharp_namespace = "Xray.App.Observatory.Burst"; -option go_package = "github.com/xtls/xray-core/app/observatory/burst"; -option java_package = "com.xray.app.observatory.burst"; -option java_multiple_files = true; - -message Config { - /* @Document The selectors for outbound under observation - */ - repeated string subject_selector = 2; - - HealthPingConfig ping_config = 3; -} - -message HealthPingConfig { - // destination url, need 204 for success return - // default https://connectivitycheck.gstatic.com/generate_204 - string destination = 1; - // connectivity check url - string connectivity = 2; - // health check interval, int64 values of time.Duration - int64 interval = 3; - // sampling count is the amount of recent ping results which are kept for calculation - int32 samplingCount = 4; - // ping timeout, int64 values of time.Duration - int64 timeout = 5; - // http method to make request - string httpMethod = 6; - -} diff --git a/app/observatory/burst/healthping.go b/app/observatory/burst/healthping.go deleted file mode 100644 index cb1c3402..00000000 --- a/app/observatory/burst/healthping.go +++ /dev/null @@ -1,268 +0,0 @@ -package burst - -import ( - "context" - "fmt" - "strings" - "sync" - "time" - - "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/features/routing" -) - -// HealthPingSettings holds settings for health Checker -type HealthPingSettings struct { - Destination string `json:"destination"` - Connectivity string `json:"connectivity"` - Interval time.Duration `json:"interval"` - SamplingCount int `json:"sampling"` - Timeout time.Duration `json:"timeout"` - HttpMethod string `json:"httpMethod"` -} - -// HealthPing is the health checker for balancers -type HealthPing struct { - ctx context.Context - dispatcher routing.Dispatcher - access sync.Mutex - ticker *time.Ticker - tickerClose chan struct{} - - Settings *HealthPingSettings - Results map[string]*HealthPingRTTS -} - -// NewHealthPing creates a new HealthPing with settings -func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *HealthPingConfig) *HealthPing { - settings := &HealthPingSettings{} - if config != nil { - - var httpMethod string - if config.HttpMethod == "" { - httpMethod = "HEAD" - } else { - httpMethod = strings.TrimSpace(config.HttpMethod) - } - - settings = &HealthPingSettings{ - Connectivity: strings.TrimSpace(config.Connectivity), - Destination: strings.TrimSpace(config.Destination), - Interval: time.Duration(config.Interval), - SamplingCount: int(config.SamplingCount), - Timeout: time.Duration(config.Timeout), - HttpMethod: httpMethod, - } - } - if settings.Destination == "" { - // Destination URL, need 204 for success return default to chromium - // https://github.com/chromium/chromium/blob/main/components/safety_check/url_constants.cc#L10 - // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/safety_check/url_constants.cc#10 - settings.Destination = "https://connectivitycheck.gstatic.com/generate_204" - } - if settings.Interval == 0 { - settings.Interval = time.Duration(1) * time.Minute - } else if settings.Interval < 10 { - errors.LogWarning(ctx, "health check interval is too small, 10s is applied") - settings.Interval = time.Duration(10) * time.Second - } - if settings.SamplingCount <= 0 { - settings.SamplingCount = 10 - } - if settings.Timeout <= 0 { - // results are saved after all health pings finish, - // a larger timeout could possibly makes checks run longer - settings.Timeout = time.Duration(5) * time.Second - } - return &HealthPing{ - ctx: ctx, - dispatcher: dispatcher, - Settings: settings, - Results: nil, - } -} - -// StartScheduler implements the HealthChecker -func (h *HealthPing) StartScheduler(selector func() ([]string, error)) { - if h.ticker != nil { - return - } - interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) - ticker := time.NewTicker(interval) - tickerClose := make(chan struct{}) - h.ticker = ticker - h.tickerClose = tickerClose - go func() { - tags, err := selector() - if err != nil { - errors.LogWarning(h.ctx, "error select outbounds for initial health check: ", err) - return - } - h.Check(tags) - }() - - go func() { - for { - go func() { - tags, err := selector() - if err != nil { - errors.LogWarning(h.ctx, "error select outbounds for scheduled health check: ", err) - return - } - h.doCheck(tags, interval, h.Settings.SamplingCount) - h.Cleanup(tags) - }() - select { - case <-ticker.C: - continue - case <-tickerClose: - return - } - } - }() -} - -// StopScheduler implements the HealthChecker -func (h *HealthPing) StopScheduler() { - if h.ticker == nil { - return - } - h.ticker.Stop() - h.ticker = nil - close(h.tickerClose) - h.tickerClose = nil -} - -// Check implements the HealthChecker -func (h *HealthPing) Check(tags []string) error { - if len(tags) == 0 { - return nil - } - errors.LogInfo(h.ctx, "perform one-time health check for tags ", tags) - h.doCheck(tags, 0, 1) - return nil -} - -type rtt struct { - handler string - value time.Duration -} - -// doCheck performs the 'rounds' amount checks in given 'duration'. You should make -// sure all tags are valid for current balancer -func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int) { - count := len(tags) * rounds - if count == 0 { - return - } - ch := make(chan *rtt, count) - - for _, tag := range tags { - handler := tag - client := newPingClient( - h.ctx, - h.dispatcher, - h.Settings.Destination, - h.Settings.Timeout, - handler, - ) - for i := 0; i < rounds; i++ { - delay := time.Duration(0) - if duration > 0 { - delay = time.Duration(dice.RollInt63n(int64(duration))) - } - time.AfterFunc(delay, func() { - errors.LogDebug(h.ctx, "checking ", handler) - delay, err := client.MeasureDelay(h.Settings.HttpMethod) - if err == nil { - ch <- &rtt{ - handler: handler, - value: delay, - } - return - } - if !h.checkConnectivity() { - errors.LogWarning(h.ctx, "network is down") - ch <- &rtt{ - handler: handler, - value: 0, - } - return - } - errors.LogWarning(h.ctx, fmt.Sprintf( - "error ping %s with %s: %s", - h.Settings.Destination, - handler, - err, - )) - ch <- &rtt{ - handler: handler, - value: rttFailed, - } - }) - } - } - for i := 0; i < count; i++ { - rtt := <-ch - if rtt.value > 0 { - // should not put results when network is down - h.PutResult(rtt.handler, rtt.value) - } - } -} - -// PutResult put a ping rtt to results -func (h *HealthPing) PutResult(tag string, rtt time.Duration) { - h.access.Lock() - defer h.access.Unlock() - if h.Results == nil { - h.Results = make(map[string]*HealthPingRTTS) - } - r, ok := h.Results[tag] - if !ok { - // validity is 2 times to sampling period, since the check are - // distributed in the time line randomly, in extreme cases, - // Previous checks are distributed on the left, and later ones - // on the right - validity := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) * 2 - r = NewHealthPingResult(h.Settings.SamplingCount, validity) - h.Results[tag] = r - } - r.Put(rtt) -} - -// Cleanup removes results of removed handlers, -// tags should be all valid tags of the Balancer now -func (h *HealthPing) Cleanup(tags []string) { - h.access.Lock() - defer h.access.Unlock() - for tag := range h.Results { - found := false - for _, v := range tags { - if tag == v { - found = true - break - } - } - if !found { - delete(h.Results, tag) - } - } -} - -// checkConnectivity checks the network connectivity, it returns -// true if network is good or "connectivity check url" not set -func (h *HealthPing) checkConnectivity() bool { - if h.Settings.Connectivity == "" { - return true - } - tester := newDirectPingClient( - h.Settings.Connectivity, - h.Settings.Timeout, - ) - if _, err := tester.MeasureDelay(h.Settings.HttpMethod); err != nil { - return false - } - return true -} diff --git a/app/observatory/burst/healthping_result.go b/app/observatory/burst/healthping_result.go deleted file mode 100644 index f48d37b6..00000000 --- a/app/observatory/burst/healthping_result.go +++ /dev/null @@ -1,143 +0,0 @@ -package burst - -import ( - "math" - "time" -) - -// HealthPingStats is the statistics of HealthPingRTTS -type HealthPingStats struct { - All int - Fail int - Deviation time.Duration - Average time.Duration - Max time.Duration - Min time.Duration -} - -// HealthPingRTTS holds ping rtts for health Checker -type HealthPingRTTS struct { - idx int - cap int - validity time.Duration - rtts []*pingRTT - - lastUpdateAt time.Time - stats *HealthPingStats -} - -type pingRTT struct { - time time.Time - value time.Duration -} - -// NewHealthPingResult returns a *HealthPingResult with specified capacity -func NewHealthPingResult(cap int, validity time.Duration) *HealthPingRTTS { - return &HealthPingRTTS{cap: cap, validity: validity} -} - -// Get gets statistics of the HealthPingRTTS -func (h *HealthPingRTTS) Get() *HealthPingStats { - return h.getStatistics() -} - -// GetWithCache get statistics and write cache for next call -// Make sure use Mutex.Lock() before calling it, RWMutex.RLock() -// is not an option since it writes cache -func (h *HealthPingRTTS) GetWithCache() *HealthPingStats { - lastPutAt := h.rtts[h.idx].time - now := time.Now() - if h.stats == nil || h.lastUpdateAt.Before(lastPutAt) || h.findOutdated(now) >= 0 { - h.stats = h.getStatistics() - h.lastUpdateAt = now - } - return h.stats -} - -// Put puts a new rtt to the HealthPingResult -func (h *HealthPingRTTS) Put(d time.Duration) { - if h.rtts == nil { - h.rtts = make([]*pingRTT, h.cap) - for i := 0; i < h.cap; i++ { - h.rtts[i] = &pingRTT{} - } - h.idx = -1 - } - h.idx = h.calcIndex(1) - now := time.Now() - h.rtts[h.idx].time = now - h.rtts[h.idx].value = d -} - -func (h *HealthPingRTTS) calcIndex(step int) int { - idx := h.idx - idx += step - if idx >= h.cap { - idx %= h.cap - } - return idx -} - -func (h *HealthPingRTTS) getStatistics() *HealthPingStats { - stats := &HealthPingStats{} - stats.Fail = 0 - stats.Max = 0 - stats.Min = rttFailed - sum := time.Duration(0) - cnt := 0 - validRTTs := make([]time.Duration, 0) - for _, rtt := range h.rtts { - switch { - case rtt.value == 0 || time.Since(rtt.time) > h.validity: - continue - case rtt.value == rttFailed: - stats.Fail++ - continue - } - cnt++ - sum += rtt.value - validRTTs = append(validRTTs, rtt.value) - if stats.Max < rtt.value { - stats.Max = rtt.value - } - if stats.Min > rtt.value { - stats.Min = rtt.value - } - } - stats.All = cnt + stats.Fail - if cnt == 0 { - stats.Min = 0 - return stats - } - stats.Average = time.Duration(int(sum) / cnt) - var std float64 - if cnt < 2 { - // no enough data for standard deviation, we assume it's half of the average rtt - // if we don't do this, standard deviation of 1 round tested nodes is 0, will always - // selected before 2 or more rounds tested nodes - std = float64(stats.Average / 2) - } else { - variance := float64(0) - for _, rtt := range validRTTs { - variance += math.Pow(float64(rtt-stats.Average), 2) - } - std = math.Sqrt(variance / float64(cnt)) - } - stats.Deviation = time.Duration(std) - return stats -} - -func (h *HealthPingRTTS) findOutdated(now time.Time) int { - for i := h.cap - 1; i < 2*h.cap; i++ { - // from oldest to latest - idx := h.calcIndex(i) - validity := h.rtts[idx].time.Add(h.validity) - if h.lastUpdateAt.After(validity) { - return idx - } - if validity.Before(now) { - return idx - } - } - return -1 -} diff --git a/app/observatory/burst/healthping_result_test.go b/app/observatory/burst/healthping_result_test.go deleted file mode 100644 index a93e22dd..00000000 --- a/app/observatory/burst/healthping_result_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package burst_test - -import ( - "math" - reflect "reflect" - "testing" - "time" - - "github.com/xtls/xray-core/app/observatory/burst" -) - -func TestHealthPingResults(t *testing.T) { - rtts := []int64{60, 140, 60, 140, 60, 60, 140, 60, 140} - hr := burst.NewHealthPingResult(4, time.Hour) - for _, rtt := range rtts { - hr.Put(time.Duration(rtt)) - } - rttFailed := time.Duration(math.MaxInt64) - expected := &burst.HealthPingStats{ - All: 4, - Fail: 0, - Deviation: 40, - Average: 100, - Max: 140, - Min: 60, - } - actual := hr.Get() - if !reflect.DeepEqual(expected, actual) { - t.Errorf("expected: %v, actual: %v", expected, actual) - } - hr.Put(rttFailed) - hr.Put(rttFailed) - expected.Fail = 2 - actual = hr.Get() - if !reflect.DeepEqual(expected, actual) { - t.Errorf("failed half-failures test, expected: %v, actual: %v", expected, actual) - } - hr.Put(rttFailed) - hr.Put(rttFailed) - expected = &burst.HealthPingStats{ - All: 4, - Fail: 4, - Deviation: 0, - Average: 0, - Max: 0, - Min: 0, - } - actual = hr.Get() - if !reflect.DeepEqual(expected, actual) { - t.Errorf("failed all-failures test, expected: %v, actual: %v", expected, actual) - } -} - -func TestHealthPingResultsIgnoreOutdated(t *testing.T) { - rtts := []int64{60, 140, 60, 140} - hr := burst.NewHealthPingResult(4, time.Duration(10)*time.Millisecond) - for i, rtt := range rtts { - if i == 2 { - // wait for previous 2 outdated - time.Sleep(time.Duration(10) * time.Millisecond) - } - hr.Put(time.Duration(rtt)) - } - hr.Get() - expected := &burst.HealthPingStats{ - All: 2, - Fail: 0, - Deviation: 40, - Average: 100, - Max: 140, - Min: 60, - } - actual := hr.Get() - if !reflect.DeepEqual(expected, actual) { - t.Errorf("failed 'half-outdated' test, expected: %v, actual: %v", expected, actual) - } - // wait for all outdated - time.Sleep(time.Duration(10) * time.Millisecond) - expected = &burst.HealthPingStats{ - All: 0, - Fail: 0, - Deviation: 0, - Average: 0, - Max: 0, - Min: 0, - } - actual = hr.Get() - if !reflect.DeepEqual(expected, actual) { - t.Errorf("failed 'outdated / not-tested' test, expected: %v, actual: %v", expected, actual) - } - - hr.Put(time.Duration(60)) - expected = &burst.HealthPingStats{ - All: 1, - Fail: 0, - // 1 sample, std=0.5rtt - Deviation: 30, - Average: 60, - Max: 60, - Min: 60, - } - actual = hr.Get() - if !reflect.DeepEqual(expected, actual) { - t.Errorf("expected: %v, actual: %v", expected, actual) - } -} diff --git a/app/observatory/burst/ping.go b/app/observatory/burst/ping.go deleted file mode 100644 index fcb40f1a..00000000 --- a/app/observatory/burst/ping.go +++ /dev/null @@ -1,79 +0,0 @@ -package burst - -import ( - "context" - "io" - "net/http" - "time" - - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/transport/internet/tagged" -) - -type pingClient struct { - destination string - httpClient *http.Client -} - -func newPingClient(ctx context.Context, dispatcher routing.Dispatcher, destination string, timeout time.Duration, handler string) *pingClient { - return &pingClient{ - destination: destination, - httpClient: newHTTPClient(ctx, dispatcher, handler, timeout), - } -} - -func newDirectPingClient(destination string, timeout time.Duration) *pingClient { - return &pingClient{ - destination: destination, - httpClient: &http.Client{Timeout: timeout}, - } -} - -func newHTTPClient(ctxv context.Context, dispatcher routing.Dispatcher, handler string, timeout time.Duration) *http.Client { - tr := &http.Transport{ - DisableKeepAlives: true, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - dest, err := net.ParseDestination(network + ":" + addr) - if err != nil { - return nil, err - } - return tagged.Dialer(ctxv, dispatcher, dest, handler) - }, - } - return &http.Client{ - Transport: tr, - Timeout: timeout, - // don't follow redirect - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } -} - -// MeasureDelay returns the delay time of the request to dest -func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) { - if s.httpClient == nil { - panic("pingClient not initialized") - } - - req, err := http.NewRequest(httpMethod, s.destination, nil) - if err != nil { - return rttFailed, err - } - - start := time.Now() - resp, err := s.httpClient.Do(req) - if err != nil { - return rttFailed, err - } - if httpMethod == http.MethodGet { - _, err = io.Copy(io.Discard, resp.Body) - if err != nil { - return rttFailed, err - } - } - resp.Body.Close() - - return time.Since(start), nil -} diff --git a/app/observatory/command/command.go b/app/observatory/command/command.go index f9bb58e3..0c1fac51 100644 --- a/app/observatory/command/command.go +++ b/app/observatory/command/command.go @@ -1,3 +1,6 @@ +//go:build !confonly +// +build !confonly + package command import ( @@ -38,7 +41,7 @@ func init() { sv := &service{v: s} err := s.RequireFeatures(func(Observatory extension.Observatory) { sv.observatory = Observatory - }, false) + }) if err != nil { return nil, err } diff --git a/app/observatory/command/command.pb.go b/app/observatory/command/command.pb.go index 4c253081..c5ffdce5 100644 --- a/app/observatory/command/command.pb.go +++ b/app/observatory/command/command.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/observatory/command/command.proto package command @@ -29,9 +29,11 @@ type GetOutboundStatusRequest struct { func (x *GetOutboundStatusRequest) Reset() { *x = GetOutboundStatusRequest{} - mi := &file_app_observatory_command_command_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_command_command_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GetOutboundStatusRequest) String() string { @@ -42,7 +44,7 @@ func (*GetOutboundStatusRequest) ProtoMessage() {} func (x *GetOutboundStatusRequest) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_command_command_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -67,9 +69,11 @@ type GetOutboundStatusResponse struct { func (x *GetOutboundStatusResponse) Reset() { *x = GetOutboundStatusResponse{} - mi := &file_app_observatory_command_command_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_command_command_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GetOutboundStatusResponse) String() string { @@ -80,7 +84,7 @@ func (*GetOutboundStatusResponse) ProtoMessage() {} func (x *GetOutboundStatusResponse) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_command_command_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -110,9 +114,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_observatory_command_command_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_command_command_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -123,7 +129,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_command_command_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -191,7 +197,7 @@ func file_app_observatory_command_command_proto_rawDescGZIP() []byte { } var file_app_observatory_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_app_observatory_command_command_proto_goTypes = []any{ +var file_app_observatory_command_command_proto_goTypes = []interface{}{ (*GetOutboundStatusRequest)(nil), // 0: xray.core.app.observatory.command.GetOutboundStatusRequest (*GetOutboundStatusResponse)(nil), // 1: xray.core.app.observatory.command.GetOutboundStatusResponse (*Config)(nil), // 2: xray.core.app.observatory.command.Config @@ -213,6 +219,44 @@ func file_app_observatory_command_command_proto_init() { if File_app_observatory_command_command_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_observatory_command_command_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetOutboundStatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_observatory_command_command_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetOutboundStatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_observatory_command_command_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/observatory/command/command_grpc.pb.go b/app/observatory/command/command_grpc.pb.go index 3b1e3be7..2a3da93e 100644 --- a/app/observatory/command/command_grpc.pb.go +++ b/app/observatory/command/command_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.2 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.12 // source: app/observatory/command/command.proto package command @@ -15,12 +15,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - ObservatoryService_GetOutboundStatus_FullMethodName = "/xray.core.app.observatory.command.ObservatoryService/GetOutboundStatus" -) +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 // ObservatoryServiceClient is the client API for ObservatoryService service. // @@ -38,9 +34,8 @@ func NewObservatoryServiceClient(cc grpc.ClientConnInterface) ObservatoryService } func (c *observatoryServiceClient) GetOutboundStatus(ctx context.Context, in *GetOutboundStatusRequest, opts ...grpc.CallOption) (*GetOutboundStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetOutboundStatusResponse) - err := c.cc.Invoke(ctx, ObservatoryService_GetOutboundStatus_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.core.app.observatory.command.ObservatoryService/GetOutboundStatus", in, out, opts...) if err != nil { return nil, err } @@ -49,24 +44,20 @@ func (c *observatoryServiceClient) GetOutboundStatus(ctx context.Context, in *Ge // ObservatoryServiceServer is the server API for ObservatoryService service. // All implementations must embed UnimplementedObservatoryServiceServer -// for forward compatibility. +// for forward compatibility type ObservatoryServiceServer interface { GetOutboundStatus(context.Context, *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) mustEmbedUnimplementedObservatoryServiceServer() } -// UnimplementedObservatoryServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedObservatoryServiceServer struct{} +// UnimplementedObservatoryServiceServer must be embedded to have forward compatible implementations. +type UnimplementedObservatoryServiceServer struct { +} func (UnimplementedObservatoryServiceServer) GetOutboundStatus(context.Context, *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOutboundStatus not implemented") } func (UnimplementedObservatoryServiceServer) mustEmbedUnimplementedObservatoryServiceServer() {} -func (UnimplementedObservatoryServiceServer) testEmbeddedByValue() {} // UnsafeObservatoryServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ObservatoryServiceServer will @@ -76,13 +67,6 @@ type UnsafeObservatoryServiceServer interface { } func RegisterObservatoryServiceServer(s grpc.ServiceRegistrar, srv ObservatoryServiceServer) { - // If the following call pancis, it indicates UnimplementedObservatoryServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&ObservatoryService_ServiceDesc, srv) } @@ -96,7 +80,7 @@ func _ObservatoryService_GetOutboundStatus_Handler(srv interface{}, ctx context. } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: ObservatoryService_GetOutboundStatus_FullMethodName, + FullMethod: "/xray.core.app.observatory.command.ObservatoryService/GetOutboundStatus", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ObservatoryServiceServer).GetOutboundStatus(ctx, req.(*GetOutboundStatusRequest)) diff --git a/app/observatory/config.pb.go b/app/observatory/config.pb.go index 1e39af6c..a6d36ead 100644 --- a/app/observatory/config.pb.go +++ b/app/observatory/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/observatory/config.proto package observatory @@ -30,9 +30,11 @@ type ObservationResult struct { func (x *ObservationResult) Reset() { *x = ObservationResult{} - mi := &file_app_observatory_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ObservationResult) String() string { @@ -43,7 +45,7 @@ func (*ObservationResult) ProtoMessage() {} func (x *ObservationResult) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -65,91 +67,6 @@ func (x *ObservationResult) GetStatus() []*OutboundStatus { return nil } -type HealthPingMeasurementResult struct { - state protoimpl.MessageState - 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() { - *x = HealthPingMeasurementResult{} - mi := &file_app_observatory_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HealthPingMeasurementResult) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthPingMeasurementResult) ProtoMessage() {} - -func (x *HealthPingMeasurementResult) ProtoReflect() protoreflect.Message { - mi := &file_app_observatory_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 HealthPingMeasurementResult.ProtoReflect.Descriptor instead. -func (*HealthPingMeasurementResult) Descriptor() ([]byte, []int) { - return file_app_observatory_config_proto_rawDescGZIP(), []int{1} -} - -func (x *HealthPingMeasurementResult) GetAll() int64 { - if x != nil { - return x.All - } - return 0 -} - -func (x *HealthPingMeasurementResult) GetFail() int64 { - if x != nil { - return x.Fail - } - return 0 -} - -func (x *HealthPingMeasurementResult) GetDeviation() int64 { - if x != nil { - return x.Deviation - } - return 0 -} - -func (x *HealthPingMeasurementResult) GetAverage() int64 { - if x != nil { - return x.Average - } - return 0 -} - -func (x *HealthPingMeasurementResult) GetMax() int64 { - if x != nil { - return x.Max - } - return 0 -} - -func (x *HealthPingMeasurementResult) GetMin() int64 { - if x != nil { - return x.Min - } - return 0 -} - type OutboundStatus struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -173,15 +90,16 @@ type OutboundStatus struct { LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"` // @Document The time this outbound is tried // @Type id.outboundTag - 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"` + LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"` } func (x *OutboundStatus) Reset() { *x = OutboundStatus{} - mi := &file_app_observatory_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *OutboundStatus) String() string { @@ -191,8 +109,8 @@ func (x *OutboundStatus) String() string { func (*OutboundStatus) ProtoMessage() {} func (x *OutboundStatus) ProtoReflect() protoreflect.Message { - mi := &file_app_observatory_config_proto_msgTypes[2] - if x != nil { + mi := &file_app_observatory_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -204,7 +122,7 @@ func (x *OutboundStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use OutboundStatus.ProtoReflect.Descriptor instead. func (*OutboundStatus) Descriptor() ([]byte, []int) { - return file_app_observatory_config_proto_rawDescGZIP(), []int{2} + return file_app_observatory_config_proto_rawDescGZIP(), []int{1} } func (x *OutboundStatus) GetAlive() bool { @@ -249,13 +167,6 @@ func (x *OutboundStatus) GetLastTryTime() int64 { return 0 } -func (x *OutboundStatus) GetHealthPing() *HealthPingMeasurementResult { - if x != nil { - return x.HealthPing - } - return nil -} - type ProbeResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -275,9 +186,11 @@ type ProbeResult struct { func (x *ProbeResult) Reset() { *x = ProbeResult{} - mi := &file_app_observatory_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ProbeResult) String() string { @@ -287,8 +200,8 @@ func (x *ProbeResult) String() string { func (*ProbeResult) ProtoMessage() {} func (x *ProbeResult) ProtoReflect() protoreflect.Message { - mi := &file_app_observatory_config_proto_msgTypes[3] - if x != nil { + mi := &file_app_observatory_config_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -300,7 +213,7 @@ func (x *ProbeResult) ProtoReflect() protoreflect.Message { // Deprecated: Use ProbeResult.ProtoReflect.Descriptor instead. func (*ProbeResult) Descriptor() ([]byte, []int) { - return file_app_observatory_config_proto_rawDescGZIP(), []int{3} + return file_app_observatory_config_proto_rawDescGZIP(), []int{2} } func (x *ProbeResult) GetAlive() bool { @@ -336,9 +249,11 @@ type Intensity struct { func (x *Intensity) Reset() { *x = Intensity{} - mi := &file_app_observatory_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Intensity) String() string { @@ -348,8 +263,8 @@ func (x *Intensity) String() string { func (*Intensity) ProtoMessage() {} func (x *Intensity) ProtoReflect() protoreflect.Message { - mi := &file_app_observatory_config_proto_msgTypes[4] - if x != nil { + mi := &file_app_observatory_config_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -361,7 +276,7 @@ func (x *Intensity) ProtoReflect() protoreflect.Message { // Deprecated: Use Intensity.ProtoReflect.Descriptor instead. func (*Intensity) Descriptor() ([]byte, []int) { - return file_app_observatory_config_proto_rawDescGZIP(), []int{4} + return file_app_observatory_config_proto_rawDescGZIP(), []int{3} } func (x *Intensity) GetProbeInterval() uint32 { @@ -385,9 +300,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_observatory_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_observatory_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -397,8 +314,8 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_app_observatory_config_proto_msgTypes[5] - if x != nil { + mi := &file_app_observatory_config_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -410,7 +327,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_app_observatory_config_proto_rawDescGZIP(), []int{5} + return file_app_observatory_config_proto_rawDescGZIP(), []int{4} } func (x *Config) GetSubjectSelector() []string { @@ -453,62 +370,47 @@ var file_app_observatory_config_proto_rawDesc = []byte{ 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, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x22, 0x9f, 0x01, 0x0a, 0x1b, 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, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, - 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, 0x65, 0x76, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6d, 0x61, - 0x78, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, - 0x6d, 0x69, 0x6e, 0x22, 0xae, 0x02, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 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, 0x12, 0x21, - 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, - 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53, - 0x65, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 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, + 0x73, 0x22, 0xd5, 0x01, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 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, 0x12, 0x21, 0x0a, 0x0c, + 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, + 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, + 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 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, 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 ( @@ -523,23 +425,21 @@ func file_app_observatory_config_proto_rawDescGZIP() []byte { return file_app_observatory_config_proto_rawDescData } -var file_app_observatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_app_observatory_config_proto_goTypes = []any{ - (*ObservationResult)(nil), // 0: xray.core.app.observatory.ObservationResult - (*HealthPingMeasurementResult)(nil), // 1: xray.core.app.observatory.HealthPingMeasurementResult - (*OutboundStatus)(nil), // 2: xray.core.app.observatory.OutboundStatus - (*ProbeResult)(nil), // 3: xray.core.app.observatory.ProbeResult - (*Intensity)(nil), // 4: xray.core.app.observatory.Intensity - (*Config)(nil), // 5: xray.core.app.observatory.Config +var file_app_observatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_app_observatory_config_proto_goTypes = []interface{}{ + (*ObservationResult)(nil), // 0: xray.core.app.observatory.ObservationResult + (*OutboundStatus)(nil), // 1: xray.core.app.observatory.OutboundStatus + (*ProbeResult)(nil), // 2: xray.core.app.observatory.ProbeResult + (*Intensity)(nil), // 3: xray.core.app.observatory.Intensity + (*Config)(nil), // 4: xray.core.app.observatory.Config } var file_app_observatory_config_proto_depIdxs = []int32{ - 2, // 0: xray.core.app.observatory.ObservationResult.status:type_name -> xray.core.app.observatory.OutboundStatus - 1, // 1: xray.core.app.observatory.OutboundStatus.health_ping:type_name -> xray.core.app.observatory.HealthPingMeasurementResult - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 1, // 0: xray.core.app.observatory.ObservationResult.status:type_name -> xray.core.app.observatory.OutboundStatus + 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_observatory_config_proto_init() } @@ -547,13 +447,75 @@ func file_app_observatory_config_proto_init() { if File_app_observatory_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_observatory_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ObservationResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_observatory_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OutboundStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_observatory_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProbeResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_observatory_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Intensity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_observatory_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_observatory_config_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/app/observatory/config.proto b/app/observatory/config.proto index fbfabad6..9ac9c549 100644 --- a/app/observatory/config.proto +++ b/app/observatory/config.proto @@ -10,15 +10,6 @@ message ObservationResult { repeated OutboundStatus status = 1; } -message HealthPingMeasurementResult { - int64 all = 1; - int64 fail = 2; - int64 deviation = 3; - int64 average = 4; - int64 max = 5; - int64 min = 6; -} - message OutboundStatus{ /* @Document Whether this outbound is usable @Restriction ReadOnlyForUser @@ -45,8 +36,6 @@ message OutboundStatus{ @Type id.outboundTag */ int64 last_try_time = 6; - - HealthPingMeasurementResult health_ping = 7; } message ProbeResult{ diff --git a/app/observatory/errors.generated.go b/app/observatory/errors.generated.go new file mode 100644 index 00000000..5cd4408a --- /dev/null +++ b/app/observatory/errors.generated.go @@ -0,0 +1,9 @@ +package observatory + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/observatory/explainErrors.go b/app/observatory/explainErrors.go index 287a78ef..9aba7923 100644 --- a/app/observatory/explainErrors.go +++ b/app/observatory/explainErrors.go @@ -8,10 +8,10 @@ type errorCollector struct { func (e *errorCollector) SubmitError(err error) { if e.errors == nil { - e.errors = errors.New("underlying connection error").Base(err) + e.errors = newError("underlying connection error").Base(err) return } - e.errors = e.errors.Base(errors.New("underlying connection error").Base(err)) + e.errors = e.errors.Base(newError("underlying connection error").Base(err)) } func newErrorCollector() *errorCollector { @@ -20,7 +20,7 @@ func newErrorCollector() *errorCollector { func (e *errorCollector) UnderlyingError() error { if e.errors == nil { - return errors.New("failed to produce report") + return newError("failed to produce report") } return e.errors } diff --git a/app/observatory/observatory.go b/app/observatory/observatory.go index 43e79a60..64726885 100644 --- a/app/observatory/observatory.go +++ b/app/observatory/observatory.go @@ -1 +1,3 @@ package observatory + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/app/observatory/observer.go b/app/observatory/observer.go index a6fba3ad..576818c4 100644 --- a/app/observatory/observer.go +++ b/app/observatory/observer.go @@ -9,8 +9,8 @@ import ( "sync" "time" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" v2net "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/signal/done" @@ -18,9 +18,7 @@ import ( "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/extension" "github.com/xtls/xray-core/features/outbound" - "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet/tagged" - "google.golang.org/protobuf/proto" ) type Observer struct { @@ -32,8 +30,7 @@ type Observer struct { finished *done.Instance - ohm outbound.Manager - dispatcher routing.Dispatcher + ohm outbound.Manager } func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) { @@ -63,7 +60,7 @@ func (o *Observer) background() { for !o.finished.Done() { hs, ok := o.ohm.(outbound.HandlerSelector) if !ok { - errors.LogInfo(o.ctx, "outbound.Manager is not a HandlerSelector") + newError("outbound.Manager is not a HandlerSelector").WriteToLog() return } @@ -130,18 +127,18 @@ func (o *Observer) probe(outbound string) ProbeResult { // MUST use Xray's built in context system dest, err := v2net.ParseDestination(network + ":" + addr) if err != nil { - return errors.New("cannot understand address").Base(err) + return newError("cannot understand address").Base(err) } trackedCtx := session.TrackedConnectionError(o.ctx, errorCollectorForRequest) - conn, err := tagged.Dialer(trackedCtx, o.dispatcher, dest, outbound) + conn, err := tagged.Dialer(trackedCtx, dest, outbound) if err != nil { - return errors.New("cannot dial remote address ", dest).Base(err) + return newError("cannot dial remote address ", dest).Base(err) } connection = conn return nil }) if taskErr != nil { - return nil, errors.New("cannot finish connection").Base(taskErr) + return nil, newError("cannot finish connection").Base(taskErr) } return connection, nil }, @@ -164,7 +161,7 @@ func (o *Observer) probe(outbound string) ProbeResult { } response, err := httpClient.Get(probeURL) if err != nil { - return errors.New("outbound failed to relay connection").Base(err) + return newError("outbound failed to relay connection").Base(err) } if response.Body != nil { response.Body.Close() @@ -174,11 +171,15 @@ func (o *Observer) probe(outbound string) ProbeResult { return nil }) if err != nil { - 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) - return ProbeResult{Alive: false, LastErrorReason: errorMessage} + fullerr := newError("underlying connection failed").Base(errorCollectorForRequest.UnderlyingError()) + fullerr = newError("with outbound handler report").Base(fullerr) + fullerr = newError("GET request failed:", err).Base(fullerr) + fullerr = newError("the outbound ", outbound, " is dead:").Base(fullerr) + fullerr = fullerr.AtInfo() + fullerr.WriteToLog() + return ProbeResult{Alive: false, LastErrorReason: fullerr.Error()} } - errors.LogInfo(o.ctx, "the outbound ", outbound, " is alive:", GETTime.Seconds()) + newError("the outbound ", outbound, " is alive:", GETTime.Seconds()).AtInfo().WriteToLog() return ProbeResult{Alive: true, Delay: GETTime.Milliseconds()} } @@ -217,19 +218,16 @@ func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int { func New(ctx context.Context, config *Config) (*Observer, error) { var outboundManager outbound.Manager - var dispatcher routing.Dispatcher - err := core.RequireFeatures(ctx, func(om outbound.Manager, rd routing.Dispatcher) { + err := core.RequireFeatures(ctx, func(om outbound.Manager) { outboundManager = om - dispatcher = rd }) if err != nil { - return nil, errors.New("Cannot get depended features").Base(err) + return nil, newError("Cannot get depended features").Base(err) } return &Observer{ - config: config, - ctx: ctx, - ohm: outboundManager, - dispatcher: dispatcher, + config: config, + ctx: ctx, + ohm: outboundManager, }, nil } diff --git a/app/policy/config.go b/app/policy/config.go index 9e5ee1c2..267307b7 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -73,7 +73,6 @@ func (p *Policy) ToCorePolicy() policy.Session { if p.Stats != nil { cp.Stats.UserUplink = p.Stats.UserUplink cp.Stats.UserDownlink = p.Stats.UserDownlink - cp.Stats.UserOnline = p.Stats.UserOnline } if p.Buffer != nil { cp.Buffer.PerConnection = p.Buffer.Connection diff --git a/app/policy/config.pb.go b/app/policy/config.pb.go index bfa83a3b..66da6d11 100644 --- a/app/policy/config.pb.go +++ b/app/policy/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/policy/config.proto package policy @@ -30,9 +30,11 @@ type Second struct { func (x *Second) Reset() { *x = Second{} - mi := &file_app_policy_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Second) String() string { @@ -43,7 +45,7 @@ func (*Second) ProtoMessage() {} func (x *Second) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -77,9 +79,11 @@ type Policy struct { func (x *Policy) Reset() { *x = Policy{} - mi := &file_app_policy_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Policy) String() string { @@ -90,7 +94,7 @@ func (*Policy) ProtoMessage() {} func (x *Policy) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -136,9 +140,11 @@ type SystemPolicy struct { func (x *SystemPolicy) Reset() { *x = SystemPolicy{} - mi := &file_app_policy_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SystemPolicy) String() string { @@ -149,7 +155,7 @@ func (*SystemPolicy) ProtoMessage() {} func (x *SystemPolicy) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -182,9 +188,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_policy_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -195,7 +203,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -238,9 +246,11 @@ type Policy_Timeout struct { func (x *Policy_Timeout) Reset() { *x = Policy_Timeout{} - mi := &file_app_policy_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Policy_Timeout) String() string { @@ -251,7 +261,7 @@ func (*Policy_Timeout) ProtoMessage() {} func (x *Policy_Timeout) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -301,14 +311,15 @@ type Policy_Stats struct { 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() { *x = Policy_Stats{} - mi := &file_app_policy_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Policy_Stats) String() string { @@ -319,7 +330,7 @@ func (*Policy_Stats) ProtoMessage() {} func (x *Policy_Stats) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -348,13 +359,6 @@ func (x *Policy_Stats) GetUserDownlink() bool { return false } -func (x *Policy_Stats) GetUserOnline() bool { - if x != nil { - return x.UserOnline - } - return false -} - type Policy_Buffer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -366,9 +370,11 @@ type Policy_Buffer struct { func (x *Policy_Buffer) Reset() { *x = Policy_Buffer{} - mi := &file_app_policy_config_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Policy_Buffer) String() string { @@ -379,7 +385,7 @@ func (*Policy_Buffer) ProtoMessage() {} func (x *Policy_Buffer) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -414,9 +420,11 @@ type SystemPolicy_Stats struct { func (x *SystemPolicy_Stats) Reset() { *x = SystemPolicy_Stats{} - mi := &file_app_policy_config_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SystemPolicy_Stats) String() string { @@ -427,7 +435,7 @@ func (*SystemPolicy_Stats) ProtoMessage() {} func (x *SystemPolicy_Stats) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[7] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -477,7 +485,7 @@ var file_app_policy_config_proto_rawDesc = []byte{ 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x1e, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc7, 0x04, 0x0a, 0x06, 0x50, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa6, 0x04, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, @@ -504,51 +512,49 @@ var file_app_policy_config_proto_rawDesc = []byte{ 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x0c, 0x64, 0x6f, - 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x6e, 0x0a, 0x05, 0x53, 0x74, + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x4d, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65, - 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 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, + 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 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 ( @@ -564,7 +570,7 @@ func file_app_policy_config_proto_rawDescGZIP() []byte { } var file_app_policy_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9) -var file_app_policy_config_proto_goTypes = []any{ +var file_app_policy_config_proto_goTypes = []interface{}{ (*Second)(nil), // 0: xray.app.policy.Second (*Policy)(nil), // 1: xray.app.policy.Policy (*SystemPolicy)(nil), // 2: xray.app.policy.SystemPolicy @@ -599,6 +605,104 @@ func file_app_policy_config_proto_init() { if File_app_policy_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_policy_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Second); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Policy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SystemPolicy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Policy_Timeout); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Policy_Stats); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Policy_Buffer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SystemPolicy_Stats); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/policy/config.proto b/app/policy/config.proto index eefcc17f..e5f29547 100644 --- a/app/policy/config.proto +++ b/app/policy/config.proto @@ -22,7 +22,6 @@ message Policy { message Stats { bool user_uplink = 1; bool user_downlink = 2; - bool user_online = 3; } message Buffer { diff --git a/app/policy/errors.generated.go b/app/policy/errors.generated.go new file mode 100644 index 00000000..1fe4d613 --- /dev/null +++ b/app/policy/errors.generated.go @@ -0,0 +1,9 @@ +package policy + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/policy/policy.go b/app/policy/policy.go index 60613a65..cbb1d90e 100644 --- a/app/policy/policy.go +++ b/app/policy/policy.go @@ -1,2 +1,4 @@ // Package policy is an implementation of policy.Manager feature. package policy + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index a86f0921..9782d6c1 100644 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -3,10 +3,7 @@ package command import ( "context" - "github.com/xtls/xray-core/app/commander" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/inbound" "github.com/xtls/xray-core/features/outbound" @@ -29,7 +26,7 @@ type OutboundOperation interface { func getInbound(handler inbound.Handler) (proxy.Inbound, error) { gi, ok := handler.(proxy.GetInbound) if !ok { - return nil, errors.New("can't get inbound proxy from handler.") + return nil, newError("can't get inbound proxy from handler.") } return gi.GetInbound(), nil } @@ -42,11 +39,11 @@ func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler inbound.Ha } um, ok := p.(proxy.UserManager) if !ok { - return errors.New("proxy is not a UserManager") + return newError("proxy is not a UserManager") } mUser, err := op.User.ToMemoryUser() if err != nil { - return errors.New("failed to parse user").Base(err) + return newError("failed to parse user").Base(err) } return um.AddUser(ctx, mUser) } @@ -59,7 +56,7 @@ func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler inbound } um, ok := p.(proxy.UserManager) if !ok { - return errors.New("proxy is not a UserManager") + return newError("proxy is not a UserManager") } return um.RemoveUser(ctx, op.Email) } @@ -85,83 +82,21 @@ func (s *handlerServer) RemoveInbound(ctx context.Context, request *RemoveInboun func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundRequest) (*AlterInboundResponse, error) { rawOperation, err := request.Operation.GetInstance() if err != nil { - return nil, errors.New("unknown operation").Base(err) + return nil, newError("unknown operation").Base(err) } operation, ok := rawOperation.(InboundOperation) if !ok { - return nil, errors.New("not an inbound operation") + return nil, newError("not an inbound operation") } handler, err := s.ihm.GetHandler(ctx, request.Tag) if err != nil { - return nil, errors.New("failed to get handler: ", request.Tag).Base(err) + return nil, newError("failed to get handler: ", request.Tag).Base(err) } return &AlterInboundResponse{}, operation.ApplyInbound(ctx, handler) } -func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) { - handlers := s.ihm.ListHandlers(ctx) - response := &ListInboundsResponse{} - if request.GetIsOnlyTags() { - for _, handler := range handlers { - response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{ - Tag: handler.Tag(), - }) - } - } else { - for _, handler := range handlers { - response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{ - Tag: handler.Tag(), - ReceiverSettings: handler.ReceiverSettings(), - ProxySettings: handler.ProxySettings(), - }) - } - } - - return response, nil -} - -func (s *handlerServer) GetInboundUsers(ctx context.Context, request *GetInboundUserRequest) (*GetInboundUserResponse, error) { - handler, err := s.ihm.GetHandler(ctx, request.Tag) - if err != nil { - return nil, errors.New("failed to get handler: ", request.Tag).Base(err) - } - p, err := getInbound(handler) - if err != nil { - return nil, err - } - um, ok := p.(proxy.UserManager) - if !ok { - return nil, errors.New("proxy is not a UserManager") - } - if len(request.Email) > 0 { - return &GetInboundUserResponse{Users: []*protocol.User{protocol.ToProtoUser(um.GetUser(ctx, request.Email))}}, nil - } - var result = make([]*protocol.User, 0, 100) - users := um.GetUsers(ctx) - for _, u := range users { - result = append(result, protocol.ToProtoUser(u)) - } - return &GetInboundUserResponse{Users: result}, nil -} - -func (s *handlerServer) GetInboundUsersCount(ctx context.Context, request *GetInboundUserRequest) (*GetInboundUsersCountResponse, error) { - handler, err := s.ihm.GetHandler(ctx, request.Tag) - if err != nil { - return nil, errors.New("failed to get handler: ", request.Tag).Base(err) - } - p, err := getInbound(handler) - if err != nil { - return nil, err - } - um, ok := p.(proxy.UserManager) - if !ok { - return nil, errors.New("proxy is not a UserManager") - } - return &GetInboundUsersCountResponse{Count: um.GetUsersCount(ctx)}, nil -} - func (s *handlerServer) AddOutbound(ctx context.Context, request *AddOutboundRequest) (*AddOutboundResponse, error) { if err := core.AddOutboundHandler(s.s, request.Outbound); err != nil { return nil, err @@ -176,34 +111,17 @@ func (s *handlerServer) RemoveOutbound(ctx context.Context, request *RemoveOutbo func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboundRequest) (*AlterOutboundResponse, error) { rawOperation, err := request.Operation.GetInstance() if err != nil { - return nil, errors.New("unknown operation").Base(err) + return nil, newError("unknown operation").Base(err) } operation, ok := rawOperation.(OutboundOperation) if !ok { - return nil, errors.New("not an outbound operation") + return nil, newError("not an outbound operation") } handler := s.ohm.GetHandler(request.Tag) return &AlterOutboundResponse{}, operation.ApplyOutbound(ctx, handler) } -func (s *handlerServer) ListOutbounds(ctx context.Context, request *ListOutboundsRequest) (*ListOutboundsResponse, error) { - handlers := s.ohm.ListHandlers(ctx) - response := &ListOutboundsResponse{} - for _, handler := range handlers { - // Ignore gRPC outbound - if _, ok := handler.(*commander.Outbound); ok { - continue - } - response.Outbounds = append(response.Outbounds, &core.OutboundHandlerConfig{ - Tag: handler.Tag(), - SenderSettings: handler.SenderSettings(), - ProxySettings: handler.ProxySettings(), - }) - } - return response, nil -} - func (s *handlerServer) mustEmbedUnimplementedHandlerServiceServer() {} type service struct { @@ -217,7 +135,7 @@ func (s *service) Register(server *grpc.Server) { common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) { hs.ihm = im hs.ohm = om - }, false)) + })) RegisterHandlerServiceServer(server, hs) // For compatibility purposes diff --git a/app/proxyman/command/command.pb.go b/app/proxyman/command/command.pb.go index 6c127960..5730955c 100644 --- a/app/proxyman/command/command.pb.go +++ b/app/proxyman/command/command.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/proxyman/command/command.proto package command @@ -33,9 +33,11 @@ type AddUserOperation struct { func (x *AddUserOperation) Reset() { *x = AddUserOperation{} - mi := &file_app_proxyman_command_command_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddUserOperation) String() string { @@ -46,7 +48,7 @@ func (*AddUserOperation) ProtoMessage() {} func (x *AddUserOperation) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -78,9 +80,11 @@ type RemoveUserOperation struct { func (x *RemoveUserOperation) Reset() { *x = RemoveUserOperation{} - mi := &file_app_proxyman_command_command_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveUserOperation) String() string { @@ -91,7 +95,7 @@ func (*RemoveUserOperation) ProtoMessage() {} func (x *RemoveUserOperation) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -123,9 +127,11 @@ type AddInboundRequest struct { func (x *AddInboundRequest) Reset() { *x = AddInboundRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddInboundRequest) String() string { @@ -136,7 +142,7 @@ func (*AddInboundRequest) ProtoMessage() {} func (x *AddInboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -166,9 +172,11 @@ type AddInboundResponse struct { func (x *AddInboundResponse) Reset() { *x = AddInboundResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddInboundResponse) String() string { @@ -179,7 +187,7 @@ func (*AddInboundResponse) ProtoMessage() {} func (x *AddInboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -204,9 +212,11 @@ type RemoveInboundRequest struct { func (x *RemoveInboundRequest) Reset() { *x = RemoveInboundRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveInboundRequest) String() string { @@ -217,7 +227,7 @@ func (*RemoveInboundRequest) ProtoMessage() {} func (x *RemoveInboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -247,9 +257,11 @@ type RemoveInboundResponse struct { func (x *RemoveInboundResponse) Reset() { *x = RemoveInboundResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveInboundResponse) String() string { @@ -260,7 +272,7 @@ func (*RemoveInboundResponse) ProtoMessage() {} func (x *RemoveInboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -286,9 +298,11 @@ type AlterInboundRequest struct { func (x *AlterInboundRequest) Reset() { *x = AlterInboundRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AlterInboundRequest) String() string { @@ -299,7 +313,7 @@ func (*AlterInboundRequest) ProtoMessage() {} func (x *AlterInboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -336,9 +350,11 @@ type AlterInboundResponse struct { func (x *AlterInboundResponse) Reset() { *x = AlterInboundResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AlterInboundResponse) String() string { @@ -349,7 +365,7 @@ func (*AlterInboundResponse) ProtoMessage() {} func (x *AlterInboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[7] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -364,239 +380,6 @@ func (*AlterInboundResponse) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{7} } -type ListInboundsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"` -} - -func (x *ListInboundsRequest) Reset() { - *x = ListInboundsRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListInboundsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListInboundsRequest) ProtoMessage() {} - -func (x *ListInboundsRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_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 ListInboundsRequest.ProtoReflect.Descriptor instead. -func (*ListInboundsRequest) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8} -} - -func (x *ListInboundsRequest) GetIsOnlyTags() bool { - if x != nil { - return x.IsOnlyTags - } - return false -} - -type ListInboundsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Inbounds []*core.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbounds,proto3" json:"inbounds,omitempty"` -} - -func (x *ListInboundsResponse) Reset() { - *x = ListInboundsResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListInboundsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListInboundsResponse) ProtoMessage() {} - -func (x *ListInboundsResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_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 ListInboundsResponse.ProtoReflect.Descriptor instead. -func (*ListInboundsResponse) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{9} -} - -func (x *ListInboundsResponse) GetInbounds() []*core.InboundHandlerConfig { - if x != nil { - return x.Inbounds - } - return nil -} - -type GetInboundUserRequest struct { - state protoimpl.MessageState - 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() { - *x = GetInboundUserRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetInboundUserRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetInboundUserRequest) ProtoMessage() {} - -func (x *GetInboundUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[10] - 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 GetInboundUserRequest.ProtoReflect.Descriptor instead. -func (*GetInboundUserRequest) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{10} -} - -func (x *GetInboundUserRequest) GetTag() string { - if x != nil { - return x.Tag - } - return "" -} - -func (x *GetInboundUserRequest) GetEmail() string { - if x != nil { - return x.Email - } - return "" -} - -type GetInboundUserResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` -} - -func (x *GetInboundUserResponse) Reset() { - *x = GetInboundUserResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetInboundUserResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetInboundUserResponse) ProtoMessage() {} - -func (x *GetInboundUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[11] - 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 GetInboundUserResponse.ProtoReflect.Descriptor instead. -func (*GetInboundUserResponse) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{11} -} - -func (x *GetInboundUserResponse) GetUsers() []*protocol.User { - if x != nil { - return x.Users - } - return nil -} - -type GetInboundUsersCountResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` -} - -func (x *GetInboundUsersCountResponse) Reset() { - *x = GetInboundUsersCountResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetInboundUsersCountResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetInboundUsersCountResponse) ProtoMessage() {} - -func (x *GetInboundUsersCountResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[12] - 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 GetInboundUsersCountResponse.ProtoReflect.Descriptor instead. -func (*GetInboundUsersCountResponse) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{12} -} - -func (x *GetInboundUsersCountResponse) GetCount() int64 { - if x != nil { - return x.Count - } - return 0 -} - type AddOutboundRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -607,9 +390,11 @@ type AddOutboundRequest struct { func (x *AddOutboundRequest) Reset() { *x = AddOutboundRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddOutboundRequest) String() string { @@ -619,8 +404,8 @@ func (x *AddOutboundRequest) String() string { func (*AddOutboundRequest) ProtoMessage() {} func (x *AddOutboundRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[13] - if x != nil { + mi := &file_app_proxyman_command_command_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -632,7 +417,7 @@ func (x *AddOutboundRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddOutboundRequest.ProtoReflect.Descriptor instead. func (*AddOutboundRequest) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{13} + return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8} } func (x *AddOutboundRequest) GetOutbound() *core.OutboundHandlerConfig { @@ -650,9 +435,11 @@ type AddOutboundResponse struct { func (x *AddOutboundResponse) Reset() { *x = AddOutboundResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddOutboundResponse) String() string { @@ -662,8 +449,8 @@ func (x *AddOutboundResponse) String() string { func (*AddOutboundResponse) ProtoMessage() {} func (x *AddOutboundResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[14] - if x != nil { + mi := &file_app_proxyman_command_command_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -675,7 +462,7 @@ func (x *AddOutboundResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddOutboundResponse.ProtoReflect.Descriptor instead. func (*AddOutboundResponse) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{14} + return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{9} } type RemoveOutboundRequest struct { @@ -688,9 +475,11 @@ type RemoveOutboundRequest struct { func (x *RemoveOutboundRequest) Reset() { *x = RemoveOutboundRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveOutboundRequest) String() string { @@ -700,8 +489,8 @@ func (x *RemoveOutboundRequest) String() string { func (*RemoveOutboundRequest) ProtoMessage() {} func (x *RemoveOutboundRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[15] - if x != nil { + mi := &file_app_proxyman_command_command_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -713,7 +502,7 @@ func (x *RemoveOutboundRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveOutboundRequest.ProtoReflect.Descriptor instead. func (*RemoveOutboundRequest) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{15} + return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{10} } func (x *RemoveOutboundRequest) GetTag() string { @@ -731,9 +520,11 @@ type RemoveOutboundResponse struct { func (x *RemoveOutboundResponse) Reset() { *x = RemoveOutboundResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveOutboundResponse) String() string { @@ -743,8 +534,8 @@ func (x *RemoveOutboundResponse) String() string { func (*RemoveOutboundResponse) ProtoMessage() {} func (x *RemoveOutboundResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[16] - if x != nil { + mi := &file_app_proxyman_command_command_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -756,7 +547,7 @@ func (x *RemoveOutboundResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveOutboundResponse.ProtoReflect.Descriptor instead. func (*RemoveOutboundResponse) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{16} + return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{11} } type AlterOutboundRequest struct { @@ -770,9 +561,11 @@ type AlterOutboundRequest struct { func (x *AlterOutboundRequest) Reset() { *x = AlterOutboundRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AlterOutboundRequest) String() string { @@ -782,8 +575,8 @@ func (x *AlterOutboundRequest) String() string { func (*AlterOutboundRequest) ProtoMessage() {} func (x *AlterOutboundRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[17] - if x != nil { + mi := &file_app_proxyman_command_command_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -795,7 +588,7 @@ func (x *AlterOutboundRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AlterOutboundRequest.ProtoReflect.Descriptor instead. func (*AlterOutboundRequest) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{17} + return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{12} } func (x *AlterOutboundRequest) GetTag() string { @@ -820,9 +613,11 @@ type AlterOutboundResponse struct { func (x *AlterOutboundResponse) Reset() { *x = AlterOutboundResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AlterOutboundResponse) String() string { @@ -832,8 +627,8 @@ func (x *AlterOutboundResponse) String() string { func (*AlterOutboundResponse) ProtoMessage() {} func (x *AlterOutboundResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[18] - if x != nil { + mi := &file_app_proxyman_command_command_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -845,88 +640,7 @@ func (x *AlterOutboundResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AlterOutboundResponse.ProtoReflect.Descriptor instead. func (*AlterOutboundResponse) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{18} -} - -type ListOutboundsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ListOutboundsRequest) Reset() { - *x = ListOutboundsRequest{} - mi := &file_app_proxyman_command_command_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListOutboundsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListOutboundsRequest) ProtoMessage() {} - -func (x *ListOutboundsRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[19] - 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 ListOutboundsRequest.ProtoReflect.Descriptor instead. -func (*ListOutboundsRequest) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{19} -} - -type ListOutboundsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Outbounds []*core.OutboundHandlerConfig `protobuf:"bytes,1,rep,name=outbounds,proto3" json:"outbounds,omitempty"` -} - -func (x *ListOutboundsResponse) Reset() { - *x = ListOutboundsResponse{} - mi := &file_app_proxyman_command_command_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListOutboundsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListOutboundsResponse) ProtoMessage() {} - -func (x *ListOutboundsResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[20] - 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 ListOutboundsResponse.ProtoReflect.Descriptor instead. -func (*ListOutboundsResponse) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{20} -} - -func (x *ListOutboundsResponse) GetOutbounds() []*core.OutboundHandlerConfig { - if x != nil { - return x.Outbounds - } - return nil + return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{13} } type Config struct { @@ -937,9 +651,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_proxyman_command_command_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_command_command_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -949,8 +665,8 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_app_proxyman_command_command_proto_msgTypes[21] - if x != nil { + mi := &file_app_proxyman_command_command_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -962,7 +678,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{21} + return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{14} } var File_app_proxyman_command_command_proto protoreflect.FileDescriptor @@ -1002,55 +718,27 @@ var file_app_proxyman_command_command_proto_rawDesc = []byte{ 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, 0x16, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x13, - 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54, 0x61, 0x67, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54, - 0x61, 0x67, 0x73, 0x22, 0x53, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x69, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, - 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x49, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 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, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, - 0x75, 0x73, 0x65, 0x72, 0x73, 0x22, 0x34, 0x0a, 0x1c, 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, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, - 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, + 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 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, + 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xc5, 0x05, 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, @@ -1073,67 +761,36 @@ var file_app_proxyman_command_command_proto_rawDesc = []byte{ 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, + 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, 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, + 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, 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, + 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, 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 ( @@ -1148,69 +805,51 @@ func file_app_proxyman_command_command_proto_rawDescGZIP() []byte { return file_app_proxyman_command_command_proto_rawDescData } -var file_app_proxyman_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 22) -var file_app_proxyman_command_command_proto_goTypes = []any{ - (*AddUserOperation)(nil), // 0: xray.app.proxyman.command.AddUserOperation - (*RemoveUserOperation)(nil), // 1: xray.app.proxyman.command.RemoveUserOperation - (*AddInboundRequest)(nil), // 2: xray.app.proxyman.command.AddInboundRequest - (*AddInboundResponse)(nil), // 3: xray.app.proxyman.command.AddInboundResponse - (*RemoveInboundRequest)(nil), // 4: xray.app.proxyman.command.RemoveInboundRequest - (*RemoveInboundResponse)(nil), // 5: xray.app.proxyman.command.RemoveInboundResponse - (*AlterInboundRequest)(nil), // 6: xray.app.proxyman.command.AlterInboundRequest - (*AlterInboundResponse)(nil), // 7: xray.app.proxyman.command.AlterInboundResponse - (*ListInboundsRequest)(nil), // 8: xray.app.proxyman.command.ListInboundsRequest - (*ListInboundsResponse)(nil), // 9: xray.app.proxyman.command.ListInboundsResponse - (*GetInboundUserRequest)(nil), // 10: xray.app.proxyman.command.GetInboundUserRequest - (*GetInboundUserResponse)(nil), // 11: xray.app.proxyman.command.GetInboundUserResponse - (*GetInboundUsersCountResponse)(nil), // 12: xray.app.proxyman.command.GetInboundUsersCountResponse - (*AddOutboundRequest)(nil), // 13: xray.app.proxyman.command.AddOutboundRequest - (*AddOutboundResponse)(nil), // 14: xray.app.proxyman.command.AddOutboundResponse - (*RemoveOutboundRequest)(nil), // 15: xray.app.proxyman.command.RemoveOutboundRequest - (*RemoveOutboundResponse)(nil), // 16: xray.app.proxyman.command.RemoveOutboundResponse - (*AlterOutboundRequest)(nil), // 17: xray.app.proxyman.command.AlterOutboundRequest - (*AlterOutboundResponse)(nil), // 18: xray.app.proxyman.command.AlterOutboundResponse - (*ListOutboundsRequest)(nil), // 19: xray.app.proxyman.command.ListOutboundsRequest - (*ListOutboundsResponse)(nil), // 20: xray.app.proxyman.command.ListOutboundsResponse - (*Config)(nil), // 21: xray.app.proxyman.command.Config - (*protocol.User)(nil), // 22: xray.common.protocol.User - (*core.InboundHandlerConfig)(nil), // 23: xray.core.InboundHandlerConfig - (*serial.TypedMessage)(nil), // 24: xray.common.serial.TypedMessage - (*core.OutboundHandlerConfig)(nil), // 25: xray.core.OutboundHandlerConfig +var file_app_proxyman_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_app_proxyman_command_command_proto_goTypes = []interface{}{ + (*AddUserOperation)(nil), // 0: xray.app.proxyman.command.AddUserOperation + (*RemoveUserOperation)(nil), // 1: xray.app.proxyman.command.RemoveUserOperation + (*AddInboundRequest)(nil), // 2: xray.app.proxyman.command.AddInboundRequest + (*AddInboundResponse)(nil), // 3: xray.app.proxyman.command.AddInboundResponse + (*RemoveInboundRequest)(nil), // 4: xray.app.proxyman.command.RemoveInboundRequest + (*RemoveInboundResponse)(nil), // 5: xray.app.proxyman.command.RemoveInboundResponse + (*AlterInboundRequest)(nil), // 6: xray.app.proxyman.command.AlterInboundRequest + (*AlterInboundResponse)(nil), // 7: xray.app.proxyman.command.AlterInboundResponse + (*AddOutboundRequest)(nil), // 8: xray.app.proxyman.command.AddOutboundRequest + (*AddOutboundResponse)(nil), // 9: xray.app.proxyman.command.AddOutboundResponse + (*RemoveOutboundRequest)(nil), // 10: xray.app.proxyman.command.RemoveOutboundRequest + (*RemoveOutboundResponse)(nil), // 11: xray.app.proxyman.command.RemoveOutboundResponse + (*AlterOutboundRequest)(nil), // 12: xray.app.proxyman.command.AlterOutboundRequest + (*AlterOutboundResponse)(nil), // 13: xray.app.proxyman.command.AlterOutboundResponse + (*Config)(nil), // 14: xray.app.proxyman.command.Config + (*protocol.User)(nil), // 15: xray.common.protocol.User + (*core.InboundHandlerConfig)(nil), // 16: xray.core.InboundHandlerConfig + (*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage + (*core.OutboundHandlerConfig)(nil), // 18: xray.core.OutboundHandlerConfig } var file_app_proxyman_command_command_proto_depIdxs = []int32{ - 22, // 0: xray.app.proxyman.command.AddUserOperation.user:type_name -> xray.common.protocol.User - 23, // 1: xray.app.proxyman.command.AddInboundRequest.inbound:type_name -> xray.core.InboundHandlerConfig - 24, // 2: xray.app.proxyman.command.AlterInboundRequest.operation:type_name -> xray.common.serial.TypedMessage - 23, // 3: xray.app.proxyman.command.ListInboundsResponse.inbounds:type_name -> xray.core.InboundHandlerConfig - 22, // 4: xray.app.proxyman.command.GetInboundUserResponse.users:type_name -> xray.common.protocol.User - 25, // 5: xray.app.proxyman.command.AddOutboundRequest.outbound:type_name -> xray.core.OutboundHandlerConfig - 24, // 6: xray.app.proxyman.command.AlterOutboundRequest.operation:type_name -> xray.common.serial.TypedMessage - 25, // 7: xray.app.proxyman.command.ListOutboundsResponse.outbounds:type_name -> xray.core.OutboundHandlerConfig - 2, // 8: xray.app.proxyman.command.HandlerService.AddInbound:input_type -> xray.app.proxyman.command.AddInboundRequest - 4, // 9: xray.app.proxyman.command.HandlerService.RemoveInbound:input_type -> xray.app.proxyman.command.RemoveInboundRequest - 6, // 10: xray.app.proxyman.command.HandlerService.AlterInbound:input_type -> xray.app.proxyman.command.AlterInboundRequest - 8, // 11: xray.app.proxyman.command.HandlerService.ListInbounds:input_type -> xray.app.proxyman.command.ListInboundsRequest - 10, // 12: xray.app.proxyman.command.HandlerService.GetInboundUsers:input_type -> xray.app.proxyman.command.GetInboundUserRequest - 10, // 13: xray.app.proxyman.command.HandlerService.GetInboundUsersCount:input_type -> xray.app.proxyman.command.GetInboundUserRequest - 13, // 14: xray.app.proxyman.command.HandlerService.AddOutbound:input_type -> xray.app.proxyman.command.AddOutboundRequest - 15, // 15: xray.app.proxyman.command.HandlerService.RemoveOutbound:input_type -> xray.app.proxyman.command.RemoveOutboundRequest - 17, // 16: xray.app.proxyman.command.HandlerService.AlterOutbound:input_type -> xray.app.proxyman.command.AlterOutboundRequest - 19, // 17: xray.app.proxyman.command.HandlerService.ListOutbounds:input_type -> xray.app.proxyman.command.ListOutboundsRequest - 3, // 18: xray.app.proxyman.command.HandlerService.AddInbound:output_type -> xray.app.proxyman.command.AddInboundResponse - 5, // 19: xray.app.proxyman.command.HandlerService.RemoveInbound:output_type -> xray.app.proxyman.command.RemoveInboundResponse - 7, // 20: xray.app.proxyman.command.HandlerService.AlterInbound:output_type -> xray.app.proxyman.command.AlterInboundResponse - 9, // 21: xray.app.proxyman.command.HandlerService.ListInbounds:output_type -> xray.app.proxyman.command.ListInboundsResponse - 11, // 22: xray.app.proxyman.command.HandlerService.GetInboundUsers:output_type -> xray.app.proxyman.command.GetInboundUserResponse - 12, // 23: xray.app.proxyman.command.HandlerService.GetInboundUsersCount:output_type -> xray.app.proxyman.command.GetInboundUsersCountResponse - 14, // 24: xray.app.proxyman.command.HandlerService.AddOutbound:output_type -> xray.app.proxyman.command.AddOutboundResponse - 16, // 25: xray.app.proxyman.command.HandlerService.RemoveOutbound:output_type -> xray.app.proxyman.command.RemoveOutboundResponse - 18, // 26: xray.app.proxyman.command.HandlerService.AlterOutbound:output_type -> xray.app.proxyman.command.AlterOutboundResponse - 20, // 27: xray.app.proxyman.command.HandlerService.ListOutbounds:output_type -> xray.app.proxyman.command.ListOutboundsResponse - 18, // [18:28] is the sub-list for method output_type - 8, // [8:18] is the sub-list for method input_type - 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 + 15, // 0: xray.app.proxyman.command.AddUserOperation.user:type_name -> xray.common.protocol.User + 16, // 1: xray.app.proxyman.command.AddInboundRequest.inbound:type_name -> xray.core.InboundHandlerConfig + 17, // 2: xray.app.proxyman.command.AlterInboundRequest.operation:type_name -> xray.common.serial.TypedMessage + 18, // 3: xray.app.proxyman.command.AddOutboundRequest.outbound:type_name -> xray.core.OutboundHandlerConfig + 17, // 4: xray.app.proxyman.command.AlterOutboundRequest.operation:type_name -> xray.common.serial.TypedMessage + 2, // 5: xray.app.proxyman.command.HandlerService.AddInbound:input_type -> xray.app.proxyman.command.AddInboundRequest + 4, // 6: xray.app.proxyman.command.HandlerService.RemoveInbound:input_type -> xray.app.proxyman.command.RemoveInboundRequest + 6, // 7: xray.app.proxyman.command.HandlerService.AlterInbound:input_type -> xray.app.proxyman.command.AlterInboundRequest + 8, // 8: xray.app.proxyman.command.HandlerService.AddOutbound:input_type -> xray.app.proxyman.command.AddOutboundRequest + 10, // 9: xray.app.proxyman.command.HandlerService.RemoveOutbound:input_type -> xray.app.proxyman.command.RemoveOutboundRequest + 12, // 10: xray.app.proxyman.command.HandlerService.AlterOutbound:input_type -> xray.app.proxyman.command.AlterOutboundRequest + 3, // 11: xray.app.proxyman.command.HandlerService.AddInbound:output_type -> xray.app.proxyman.command.AddInboundResponse + 5, // 12: xray.app.proxyman.command.HandlerService.RemoveInbound:output_type -> xray.app.proxyman.command.RemoveInboundResponse + 7, // 13: xray.app.proxyman.command.HandlerService.AlterInbound:output_type -> xray.app.proxyman.command.AlterInboundResponse + 9, // 14: xray.app.proxyman.command.HandlerService.AddOutbound:output_type -> xray.app.proxyman.command.AddOutboundResponse + 11, // 15: xray.app.proxyman.command.HandlerService.RemoveOutbound:output_type -> xray.app.proxyman.command.RemoveOutboundResponse + 13, // 16: xray.app.proxyman.command.HandlerService.AlterOutbound:output_type -> xray.app.proxyman.command.AlterOutboundResponse + 11, // [11:17] is the sub-list for method output_type + 5, // [5:11] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_app_proxyman_command_command_proto_init() } @@ -1218,13 +857,195 @@ func file_app_proxyman_command_command_proto_init() { if File_app_proxyman_command_command_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_proxyman_command_command_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddUserOperation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveUserOperation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddInboundRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddInboundResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveInboundRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveInboundResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AlterInboundRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AlterInboundResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddOutboundRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddOutboundResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveOutboundRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveOutboundResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AlterOutboundRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AlterOutboundResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_command_command_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_proxyman_command_command_proto_rawDesc, NumEnums: 0, - NumMessages: 22, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/app/proxyman/command/command.proto b/app/proxyman/command/command.proto index 71f8f0dc..5a09bd88 100644 --- a/app/proxyman/command/command.proto +++ b/app/proxyman/command/command.proto @@ -37,27 +37,6 @@ message AlterInboundRequest { message AlterInboundResponse {} -message ListInboundsRequest { - bool isOnlyTags = 1; -} - -message ListInboundsResponse { - repeated core.InboundHandlerConfig inbounds = 1; -} - -message GetInboundUserRequest { - string tag = 1; - string email = 2; -} - -message GetInboundUserResponse { - repeated xray.common.protocol.User users = 1; -} - -message GetInboundUsersCountResponse { - int64 count = 1; -} - message AddOutboundRequest { core.OutboundHandlerConfig outbound = 1; } @@ -77,12 +56,6 @@ message AlterOutboundRequest { message AlterOutboundResponse {} -message ListOutboundsRequest {} - -message ListOutboundsResponse { - repeated core.OutboundHandlerConfig outbounds = 1; -} - service HandlerService { rpc AddInbound(AddInboundRequest) returns (AddInboundResponse) {} @@ -90,19 +63,11 @@ service HandlerService { rpc AlterInbound(AlterInboundRequest) returns (AlterInboundResponse) {} - rpc ListInbounds(ListInboundsRequest) returns (ListInboundsResponse) {} - - rpc GetInboundUsers(GetInboundUserRequest) returns (GetInboundUserResponse) {} - - rpc GetInboundUsersCount(GetInboundUserRequest) returns (GetInboundUsersCountResponse) {} - rpc AddOutbound(AddOutboundRequest) returns (AddOutboundResponse) {} rpc RemoveOutbound(RemoveOutboundRequest) returns (RemoveOutboundResponse) {} rpc AlterOutbound(AlterOutboundRequest) returns (AlterOutboundResponse) {} - - rpc ListOutbounds(ListOutboundsRequest) returns (ListOutboundsResponse) {} } message Config {} diff --git a/app/proxyman/command/command_grpc.pb.go b/app/proxyman/command/command_grpc.pb.go index 8bc48f0a..979b101f 100644 --- a/app/proxyman/command/command_grpc.pb.go +++ b/app/proxyman/command/command_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.2 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.12 // source: app/proxyman/command/command.proto package command @@ -15,21 +15,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - HandlerService_AddInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AddInbound" - HandlerService_RemoveInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/RemoveInbound" - HandlerService_AlterInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AlterInbound" - HandlerService_ListInbounds_FullMethodName = "/xray.app.proxyman.command.HandlerService/ListInbounds" - HandlerService_GetInboundUsers_FullMethodName = "/xray.app.proxyman.command.HandlerService/GetInboundUsers" - HandlerService_GetInboundUsersCount_FullMethodName = "/xray.app.proxyman.command.HandlerService/GetInboundUsersCount" - HandlerService_AddOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AddOutbound" - HandlerService_RemoveOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/RemoveOutbound" - HandlerService_AlterOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AlterOutbound" - HandlerService_ListOutbounds_FullMethodName = "/xray.app.proxyman.command.HandlerService/ListOutbounds" -) +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 // HandlerServiceClient is the client API for HandlerService service. // @@ -38,13 +25,9 @@ type HandlerServiceClient interface { AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) - ListInbounds(ctx context.Context, in *ListInboundsRequest, opts ...grpc.CallOption) (*ListInboundsResponse, error) - GetInboundUsers(ctx context.Context, in *GetInboundUserRequest, opts ...grpc.CallOption) (*GetInboundUserResponse, error) - GetInboundUsersCount(ctx context.Context, in *GetInboundUserRequest, opts ...grpc.CallOption) (*GetInboundUsersCountResponse, error) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) - ListOutbounds(ctx context.Context, in *ListOutboundsRequest, opts ...grpc.CallOption) (*ListOutboundsResponse, error) } type handlerServiceClient struct { @@ -56,9 +39,8 @@ func NewHandlerServiceClient(cc grpc.ClientConnInterface) HandlerServiceClient { } func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AddInboundResponse) - err := c.cc.Invoke(ctx, HandlerService_AddInbound_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AddInbound", in, out, opts...) if err != nil { return nil, err } @@ -66,9 +48,8 @@ func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundReq } func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RemoveInboundResponse) - err := c.cc.Invoke(ctx, HandlerService_RemoveInbound_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/RemoveInbound", in, out, opts...) if err != nil { return nil, err } @@ -76,39 +57,8 @@ func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInbo } func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AlterInboundResponse) - err := c.cc.Invoke(ctx, HandlerService_AlterInbound_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *handlerServiceClient) ListInbounds(ctx context.Context, in *ListInboundsRequest, opts ...grpc.CallOption) (*ListInboundsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListInboundsResponse) - err := c.cc.Invoke(ctx, HandlerService_ListInbounds_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *handlerServiceClient) GetInboundUsers(ctx context.Context, in *GetInboundUserRequest, opts ...grpc.CallOption) (*GetInboundUserResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetInboundUserResponse) - err := c.cc.Invoke(ctx, HandlerService_GetInboundUsers_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *handlerServiceClient) GetInboundUsersCount(ctx context.Context, in *GetInboundUserRequest, opts ...grpc.CallOption) (*GetInboundUsersCountResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetInboundUsersCountResponse) - err := c.cc.Invoke(ctx, HandlerService_GetInboundUsersCount_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AlterInbound", in, out, opts...) if err != nil { return nil, err } @@ -116,9 +66,8 @@ func (c *handlerServiceClient) GetInboundUsersCount(ctx context.Context, in *Get } func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AddOutboundResponse) - err := c.cc.Invoke(ctx, HandlerService_AddOutbound_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AddOutbound", in, out, opts...) if err != nil { return nil, err } @@ -126,9 +75,8 @@ func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundR } func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RemoveOutboundResponse) - err := c.cc.Invoke(ctx, HandlerService_RemoveOutbound_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/RemoveOutbound", in, out, opts...) if err != nil { return nil, err } @@ -136,19 +84,8 @@ func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOut } func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AlterOutboundResponse) - err := c.cc.Invoke(ctx, HandlerService_AlterOutbound_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *handlerServiceClient) ListOutbounds(ctx context.Context, in *ListOutboundsRequest, opts ...grpc.CallOption) (*ListOutboundsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListOutboundsResponse) - err := c.cc.Invoke(ctx, HandlerService_ListOutbounds_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AlterOutbound", in, out, opts...) if err != nil { return nil, err } @@ -157,27 +94,20 @@ func (c *handlerServiceClient) ListOutbounds(ctx context.Context, in *ListOutbou // HandlerServiceServer is the server API for HandlerService service. // All implementations must embed UnimplementedHandlerServiceServer -// for forward compatibility. +// for forward compatibility type HandlerServiceServer interface { AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) - ListInbounds(context.Context, *ListInboundsRequest) (*ListInboundsResponse, error) - GetInboundUsers(context.Context, *GetInboundUserRequest) (*GetInboundUserResponse, error) - GetInboundUsersCount(context.Context, *GetInboundUserRequest) (*GetInboundUsersCountResponse, error) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) - ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error) mustEmbedUnimplementedHandlerServiceServer() } -// UnimplementedHandlerServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedHandlerServiceServer struct{} +// UnimplementedHandlerServiceServer must be embedded to have forward compatible implementations. +type UnimplementedHandlerServiceServer struct { +} func (UnimplementedHandlerServiceServer) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddInbound not implemented") @@ -188,15 +118,6 @@ func (UnimplementedHandlerServiceServer) RemoveInbound(context.Context, *RemoveI func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AlterInbound not implemented") } -func (UnimplementedHandlerServiceServer) ListInbounds(context.Context, *ListInboundsRequest) (*ListInboundsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListInbounds not implemented") -} -func (UnimplementedHandlerServiceServer) GetInboundUsers(context.Context, *GetInboundUserRequest) (*GetInboundUserResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetInboundUsers not implemented") -} -func (UnimplementedHandlerServiceServer) GetInboundUsersCount(context.Context, *GetInboundUserRequest) (*GetInboundUsersCountResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetInboundUsersCount not implemented") -} func (UnimplementedHandlerServiceServer) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddOutbound not implemented") } @@ -206,11 +127,7 @@ func (UnimplementedHandlerServiceServer) RemoveOutbound(context.Context, *Remove func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AlterOutbound not implemented") } -func (UnimplementedHandlerServiceServer) ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListOutbounds not implemented") -} func (UnimplementedHandlerServiceServer) mustEmbedUnimplementedHandlerServiceServer() {} -func (UnimplementedHandlerServiceServer) testEmbeddedByValue() {} // UnsafeHandlerServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to HandlerServiceServer will @@ -220,13 +137,6 @@ type UnsafeHandlerServiceServer interface { } func RegisterHandlerServiceServer(s grpc.ServiceRegistrar, srv HandlerServiceServer) { - // If the following call pancis, it indicates UnimplementedHandlerServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&HandlerService_ServiceDesc, srv) } @@ -240,7 +150,7 @@ func _HandlerService_AddInbound_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: HandlerService_AddInbound_FullMethodName, + FullMethod: "/xray.app.proxyman.command.HandlerService/AddInbound", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest)) @@ -258,7 +168,7 @@ func _HandlerService_RemoveInbound_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: HandlerService_RemoveInbound_FullMethodName, + FullMethod: "/xray.app.proxyman.command.HandlerService/RemoveInbound", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest)) @@ -276,7 +186,7 @@ func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: HandlerService_AlterInbound_FullMethodName, + FullMethod: "/xray.app.proxyman.command.HandlerService/AlterInbound", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest)) @@ -284,60 +194,6 @@ func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _HandlerService_ListInbounds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListInboundsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(HandlerServiceServer).ListInbounds(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: HandlerService_ListInbounds_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HandlerServiceServer).ListInbounds(ctx, req.(*ListInboundsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _HandlerService_GetInboundUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetInboundUserRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(HandlerServiceServer).GetInboundUsers(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: HandlerService_GetInboundUsers_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HandlerServiceServer).GetInboundUsers(ctx, req.(*GetInboundUserRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _HandlerService_GetInboundUsersCount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetInboundUserRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(HandlerServiceServer).GetInboundUsersCount(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: HandlerService_GetInboundUsersCount_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HandlerServiceServer).GetInboundUsersCount(ctx, req.(*GetInboundUserRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddOutboundRequest) if err := dec(in); err != nil { @@ -348,7 +204,7 @@ func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, d } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: HandlerService_AddOutbound_FullMethodName, + FullMethod: "/xray.app.proxyman.command.HandlerService/AddOutbound", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest)) @@ -366,7 +222,7 @@ func _HandlerService_RemoveOutbound_Handler(srv interface{}, ctx context.Context } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: HandlerService_RemoveOutbound_FullMethodName, + FullMethod: "/xray.app.proxyman.command.HandlerService/RemoveOutbound", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest)) @@ -384,7 +240,7 @@ func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: HandlerService_AlterOutbound_FullMethodName, + FullMethod: "/xray.app.proxyman.command.HandlerService/AlterOutbound", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest)) @@ -392,24 +248,6 @@ func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _HandlerService_ListOutbounds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListOutboundsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(HandlerServiceServer).ListOutbounds(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: HandlerService_ListOutbounds_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HandlerServiceServer).ListOutbounds(ctx, req.(*ListOutboundsRequest)) - } - return interceptor(ctx, in, info, handler) -} - // HandlerService_ServiceDesc is the grpc.ServiceDesc for HandlerService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -429,18 +267,6 @@ var HandlerService_ServiceDesc = grpc.ServiceDesc{ MethodName: "AlterInbound", Handler: _HandlerService_AlterInbound_Handler, }, - { - MethodName: "ListInbounds", - Handler: _HandlerService_ListInbounds_Handler, - }, - { - MethodName: "GetInboundUsers", - Handler: _HandlerService_GetInboundUsers_Handler, - }, - { - MethodName: "GetInboundUsersCount", - Handler: _HandlerService_GetInboundUsersCount_Handler, - }, { MethodName: "AddOutbound", Handler: _HandlerService_AddOutbound_Handler, @@ -453,10 +279,6 @@ var HandlerService_ServiceDesc = grpc.ServiceDesc{ MethodName: "AlterOutbound", Handler: _HandlerService_AlterOutbound_Handler, }, - { - MethodName: "ListOutbounds", - Handler: _HandlerService_ListOutbounds_Handler, - }, }, Streams: []grpc.StreamDesc{}, Metadata: "app/proxyman/command/command.proto", diff --git a/app/proxyman/command/doc.go b/app/proxyman/command/doc.go index d47dcf0d..2aa6d95f 100644 --- a/app/proxyman/command/doc.go +++ b/app/proxyman/command/doc.go @@ -1 +1,3 @@ package command + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/app/proxyman/command/errors.generated.go b/app/proxyman/command/errors.generated.go new file mode 100644 index 00000000..a1305932 --- /dev/null +++ b/app/proxyman/command/errors.generated.go @@ -0,0 +1,9 @@ +package command + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/proxyman/config.go b/app/proxyman/config.go index 76d7a194..73c00c74 100644 --- a/app/proxyman/config.go +++ b/app/proxyman/config.go @@ -19,5 +19,21 @@ func (c *ReceiverConfig) GetEffectiveSniffingSettings() *SniffingConfig { return c.SniffingSettings } + if len(c.DomainOverride) > 0 { + var p []string + for _, kd := range c.DomainOverride { + switch kd { + case KnownProtocols_HTTP: + p = append(p, "http") + case KnownProtocols_TLS: + p = append(p, "tls") + } + } + return &SniffingConfig{ + Enabled: true, + DestinationOverride: p, + } + } + return nil } diff --git a/app/proxyman/config.pb.go b/app/proxyman/config.pb.go index d644e3d1..2dfe2931 100644 --- a/app/proxyman/config.pb.go +++ b/app/proxyman/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/proxyman/config.proto package proxyman @@ -23,6 +23,52 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type KnownProtocols int32 + +const ( + KnownProtocols_HTTP KnownProtocols = 0 + KnownProtocols_TLS KnownProtocols = 1 +) + +// Enum value maps for KnownProtocols. +var ( + KnownProtocols_name = map[int32]string{ + 0: "HTTP", + 1: "TLS", + } + KnownProtocols_value = map[string]int32{ + "HTTP": 0, + "TLS": 1, + } +) + +func (x KnownProtocols) Enum() *KnownProtocols { + p := new(KnownProtocols) + *p = x + return p +} + +func (x KnownProtocols) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (KnownProtocols) Descriptor() protoreflect.EnumDescriptor { + return file_app_proxyman_config_proto_enumTypes[0].Descriptor() +} + +func (KnownProtocols) Type() protoreflect.EnumType { + return &file_app_proxyman_config_proto_enumTypes[0] +} + +func (x KnownProtocols) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use KnownProtocols.Descriptor instead. +func (KnownProtocols) EnumDescriptor() ([]byte, []int) { + return file_app_proxyman_config_proto_rawDescGZIP(), []int{0} +} + type AllocationStrategy_Type int32 const ( @@ -59,11 +105,11 @@ func (x AllocationStrategy_Type) String() string { } func (AllocationStrategy_Type) Descriptor() protoreflect.EnumDescriptor { - return file_app_proxyman_config_proto_enumTypes[0].Descriptor() + return file_app_proxyman_config_proto_enumTypes[1].Descriptor() } func (AllocationStrategy_Type) Type() protoreflect.EnumType { - return &file_app_proxyman_config_proto_enumTypes[0] + return &file_app_proxyman_config_proto_enumTypes[1] } func (x AllocationStrategy_Type) Number() protoreflect.EnumNumber { @@ -83,9 +129,11 @@ type InboundConfig struct { func (x *InboundConfig) Reset() { *x = InboundConfig{} - mi := &file_app_proxyman_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *InboundConfig) String() string { @@ -96,7 +144,7 @@ func (*InboundConfig) ProtoMessage() {} func (x *InboundConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -127,9 +175,11 @@ type AllocationStrategy struct { func (x *AllocationStrategy) Reset() { *x = AllocationStrategy{} - mi := &file_app_proxyman_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AllocationStrategy) String() string { @@ -140,7 +190,7 @@ func (*AllocationStrategy) ProtoMessage() {} func (x *AllocationStrategy) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -196,9 +246,11 @@ type SniffingConfig struct { func (x *SniffingConfig) Reset() { *x = SniffingConfig{} - mi := &file_app_proxyman_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SniffingConfig) String() string { @@ -209,7 +261,7 @@ func (*SniffingConfig) ProtoMessage() {} func (x *SniffingConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -271,14 +323,21 @@ type ReceiverConfig struct { AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy,proto3" json:"allocation_strategy,omitempty"` StreamSettings *internet.StreamConfig `protobuf:"bytes,4,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` ReceiveOriginalDestination bool `protobuf:"varint,5,opt,name=receive_original_destination,json=receiveOriginalDestination,proto3" json:"receive_original_destination,omitempty"` - SniffingSettings *SniffingConfig `protobuf:"bytes,7,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"` + // Override domains for the given protocol. + // Deprecated. Use sniffing_settings. + // + // Deprecated: Do not use. + DomainOverride []KnownProtocols `protobuf:"varint,7,rep,packed,name=domain_override,json=domainOverride,proto3,enum=xray.app.proxyman.KnownProtocols" json:"domain_override,omitempty"` + SniffingSettings *SniffingConfig `protobuf:"bytes,8,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"` } func (x *ReceiverConfig) Reset() { *x = ReceiverConfig{} - mi := &file_app_proxyman_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ReceiverConfig) String() string { @@ -289,7 +348,7 @@ func (*ReceiverConfig) ProtoMessage() {} func (x *ReceiverConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -339,6 +398,14 @@ func (x *ReceiverConfig) GetReceiveOriginalDestination() bool { return false } +// Deprecated: Do not use. +func (x *ReceiverConfig) GetDomainOverride() []KnownProtocols { + if x != nil { + return x.DomainOverride + } + return nil +} + func (x *ReceiverConfig) GetSniffingSettings() *SniffingConfig { if x != nil { return x.SniffingSettings @@ -358,9 +425,11 @@ type InboundHandlerConfig struct { func (x *InboundHandlerConfig) Reset() { *x = InboundHandlerConfig{} - mi := &file_app_proxyman_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *InboundHandlerConfig) String() string { @@ -371,7 +440,7 @@ func (*InboundHandlerConfig) ProtoMessage() {} func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -415,9 +484,11 @@ type OutboundConfig struct { func (x *OutboundConfig) Reset() { *x = OutboundConfig{} - mi := &file_app_proxyman_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *OutboundConfig) String() string { @@ -428,7 +499,7 @@ func (*OutboundConfig) ProtoMessage() {} func (x *OutboundConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -453,14 +524,15 @@ type SenderConfig struct { StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"` - ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"` } func (x *SenderConfig) Reset() { *x = SenderConfig{} - mi := &file_app_proxyman_config_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SenderConfig) String() string { @@ -471,7 +543,7 @@ func (*SenderConfig) ProtoMessage() {} func (x *SenderConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -514,13 +586,6 @@ func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig { return nil } -func (x *SenderConfig) GetViaCidr() string { - if x != nil { - return x.ViaCidr - } - return "" -} - type MultiplexingConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -529,18 +594,16 @@ type MultiplexingConfig struct { // Whether or not Mux is enabled. Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` // Max number of concurrent connections that one Mux connection can handle. - Concurrency int32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"` - // Transport XUDP in another Mux. - XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"` - // "reject" (default), "allow" or "skip". - XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"` + Concurrency uint32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"` } func (x *MultiplexingConfig) Reset() { *x = MultiplexingConfig{} - mi := &file_app_proxyman_config_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *MultiplexingConfig) String() string { @@ -551,7 +614,7 @@ func (*MultiplexingConfig) ProtoMessage() {} func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[7] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -573,27 +636,13 @@ func (x *MultiplexingConfig) GetEnabled() bool { return false } -func (x *MultiplexingConfig) GetConcurrency() int32 { +func (x *MultiplexingConfig) GetConcurrency() uint32 { if x != nil { return x.Concurrency } return 0 } -func (x *MultiplexingConfig) GetXudpConcurrency() int32 { - if x != nil { - return x.XudpConcurrency - } - return 0 -} - -func (x *MultiplexingConfig) GetXudpProxyUDP443() string { - if x != nil { - return x.XudpProxyUDP443 - } - return "" -} - type AllocationStrategy_AllocationStrategyConcurrency struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -604,9 +653,11 @@ type AllocationStrategy_AllocationStrategyConcurrency struct { 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) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AllocationStrategy_AllocationStrategyConcurrency) String() string { @@ -617,7 +668,7 @@ func (*AllocationStrategy_AllocationStrategyConcurrency) ProtoMessage() {} func (x *AllocationStrategy_AllocationStrategyConcurrency) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[8] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -649,9 +700,11 @@ type AllocationStrategy_AllocationStrategyRefresh struct { 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) + if protoimpl.UnsafeEnabled { + mi := &file_app_proxyman_config_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AllocationStrategy_AllocationStrategyRefresh) String() string { @@ -662,7 +715,7 @@ func (*AllocationStrategy_AllocationStrategyRefresh) ProtoMessage() {} func (x *AllocationStrategy_AllocationStrategyRefresh) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[9] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -738,7 +791,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 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, + 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x8d, 0x04, 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, @@ -761,8 +814,13 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 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, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, + 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, + 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4b, 0x6e, 0x6f, 0x77, + 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 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, + 0x6e, 0x67, 0x73, 0x18, 0x08, 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, @@ -779,7 +837,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xcb, 0x02, 0x0a, 0x0c, 0x53, 0x65, + 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, @@ -798,25 +856,21 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, - 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 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, 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, + 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x50, 0x0a, 0x12, + 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, + 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23, + 0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, + 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c, + 0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, + 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, + 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -831,46 +885,48 @@ func file_app_proxyman_config_proto_rawDescGZIP() []byte { return file_app_proxyman_config_proto_rawDescData } -var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10) -var file_app_proxyman_config_proto_goTypes = []any{ - (AllocationStrategy_Type)(0), // 0: xray.app.proxyman.AllocationStrategy.Type - (*InboundConfig)(nil), // 1: xray.app.proxyman.InboundConfig - (*AllocationStrategy)(nil), // 2: xray.app.proxyman.AllocationStrategy - (*SniffingConfig)(nil), // 3: xray.app.proxyman.SniffingConfig - (*ReceiverConfig)(nil), // 4: xray.app.proxyman.ReceiverConfig - (*InboundHandlerConfig)(nil), // 5: xray.app.proxyman.InboundHandlerConfig - (*OutboundConfig)(nil), // 6: xray.app.proxyman.OutboundConfig - (*SenderConfig)(nil), // 7: xray.app.proxyman.SenderConfig - (*MultiplexingConfig)(nil), // 8: xray.app.proxyman.MultiplexingConfig - (*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 9: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency - (*AllocationStrategy_AllocationStrategyRefresh)(nil), // 10: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh - (*net.PortList)(nil), // 11: xray.common.net.PortList - (*net.IPOrDomain)(nil), // 12: xray.common.net.IPOrDomain - (*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig - (*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage - (*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig +var file_app_proxyman_config_proto_goTypes = []interface{}{ + (KnownProtocols)(0), // 0: xray.app.proxyman.KnownProtocols + (AllocationStrategy_Type)(0), // 1: xray.app.proxyman.AllocationStrategy.Type + (*InboundConfig)(nil), // 2: xray.app.proxyman.InboundConfig + (*AllocationStrategy)(nil), // 3: xray.app.proxyman.AllocationStrategy + (*SniffingConfig)(nil), // 4: xray.app.proxyman.SniffingConfig + (*ReceiverConfig)(nil), // 5: xray.app.proxyman.ReceiverConfig + (*InboundHandlerConfig)(nil), // 6: xray.app.proxyman.InboundHandlerConfig + (*OutboundConfig)(nil), // 7: xray.app.proxyman.OutboundConfig + (*SenderConfig)(nil), // 8: xray.app.proxyman.SenderConfig + (*MultiplexingConfig)(nil), // 9: xray.app.proxyman.MultiplexingConfig + (*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 10: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency + (*AllocationStrategy_AllocationStrategyRefresh)(nil), // 11: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh + (*net.PortList)(nil), // 12: xray.common.net.PortList + (*net.IPOrDomain)(nil), // 13: xray.common.net.IPOrDomain + (*internet.StreamConfig)(nil), // 14: xray.transport.internet.StreamConfig + (*serial.TypedMessage)(nil), // 15: xray.common.serial.TypedMessage + (*internet.ProxyConfig)(nil), // 16: xray.transport.internet.ProxyConfig } var file_app_proxyman_config_proto_depIdxs = []int32{ - 0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type - 9, // 1: xray.app.proxyman.AllocationStrategy.concurrency:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency - 10, // 2: xray.app.proxyman.AllocationStrategy.refresh:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh - 11, // 3: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList - 12, // 4: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain - 2, // 5: xray.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> xray.app.proxyman.AllocationStrategy - 13, // 6: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig - 3, // 7: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig - 14, // 8: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage - 14, // 9: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage - 12, // 10: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain - 13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig - 15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig - 8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 1, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type + 10, // 1: xray.app.proxyman.AllocationStrategy.concurrency:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency + 11, // 2: xray.app.proxyman.AllocationStrategy.refresh:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh + 12, // 3: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList + 13, // 4: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain + 3, // 5: xray.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> xray.app.proxyman.AllocationStrategy + 14, // 6: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig + 0, // 7: xray.app.proxyman.ReceiverConfig.domain_override:type_name -> xray.app.proxyman.KnownProtocols + 4, // 8: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig + 15, // 9: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage + 15, // 10: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage + 13, // 11: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain + 14, // 12: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig + 16, // 13: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig + 9, // 14: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_app_proxyman_config_proto_init() } @@ -878,12 +934,134 @@ func file_app_proxyman_config_proto_init() { if File_app_proxyman_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_proxyman_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InboundConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AllocationStrategy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SniffingConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReceiverConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InboundHandlerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OutboundConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SenderConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MultiplexingConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AllocationStrategy_AllocationStrategyConcurrency); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_proxyman_config_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AllocationStrategy_AllocationStrategyRefresh); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_proxyman_config_proto_rawDesc, - NumEnums: 1, + NumEnums: 2, NumMessages: 10, NumExtensions: 0, NumServices: 0, diff --git a/app/proxyman/config.proto b/app/proxyman/config.proto index 635ffad2..24216d2c 100644 --- a/app/proxyman/config.proto +++ b/app/proxyman/config.proto @@ -40,6 +40,11 @@ message AllocationStrategy { AllocationStrategyRefresh refresh = 3; } +enum KnownProtocols { + HTTP = 0; + TLS = 1; +} + message SniffingConfig { // Whether or not to enable content sniffing on an inbound connection. bool enabled = 1; @@ -66,7 +71,10 @@ message ReceiverConfig { xray.transport.internet.StreamConfig stream_settings = 4; bool receive_original_destination = 5; reserved 6; - SniffingConfig sniffing_settings = 7; + // Override domains for the given protocol. + // Deprecated. Use sniffing_settings. + repeated KnownProtocols domain_override = 7 [ deprecated = true ]; + SniffingConfig sniffing_settings = 8; } message InboundHandlerConfig { @@ -83,16 +91,11 @@ message SenderConfig { xray.transport.internet.StreamConfig stream_settings = 2; xray.transport.internet.ProxyConfig proxy_settings = 3; MultiplexingConfig multiplex_settings = 4; - string via_cidr = 5; } message MultiplexingConfig { // Whether or not Mux is enabled. bool enabled = 1; // Max number of concurrent connections that one Mux connection can handle. - int32 concurrency = 2; - // Transport XUDP in another Mux. - int32 xudpConcurrency = 3; - // "reject" (default), "allow" or "skip". - string xudpProxyUDP443 = 4; + uint32 concurrency = 2; } diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go index f6a769fb..f422e626 100644 --- a/app/proxyman/inbound/always.go +++ b/app/proxyman/inbound/always.go @@ -9,13 +9,11 @@ import ( "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/core" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/transport/internet" - "google.golang.org/protobuf/proto" ) func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) { @@ -44,12 +42,10 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) } type AlwaysOnInboundHandler struct { - proxyConfig interface{} - receiverConfig *proxyman.ReceiverConfig - proxy proxy.Inbound - workers []worker - mux *mux.Server - tag string + proxy proxy.Inbound + workers []worker + mux *mux.Server + tag string } func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) { @@ -59,15 +55,13 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig * } p, ok := rawProxy.(proxy.Inbound) if !ok { - return nil, errors.New("not an inbound proxy.") + return nil, newError("not an inbound proxy.") } h := &AlwaysOnInboundHandler{ - receiverConfig: receiverConfig, - proxyConfig: proxyConfig, - proxy: p, - mux: mux.NewServer(ctx), - tag: tag, + proxy: p, + mux: mux.NewServer(ctx), + tag: tag, } uplinkCounter, downlinkCounter := getStatCounter(core.MustFromContext(ctx), tag) @@ -81,7 +75,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig * mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings) if err != nil { - return nil, errors.New("failed to parse stream config").Base(err).AtWarning() + return nil, newError("failed to parse stream config").Base(err).AtWarning() } if receiverConfig.ReceiveOriginalDestination { @@ -95,7 +89,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig * } if pl == nil { if net.HasNetwork(nl, net.Network_UNIX) { - errors.LogDebug(ctx, "creating unix domain socket worker on ", address) + newError("creating unix domain socket worker on ", address).AtDebug().WriteToLog() worker := &dsWorker{ address: address, @@ -115,7 +109,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig * for _, pr := range pl.Range { for port := pr.From; port <= pr.To; port++ { if net.HasNetwork(nl, net.Network_TCP) { - errors.LogDebug(ctx, "creating stream worker on ", address, ":", port) + newError("creating stream worker on ", address, ":", port).AtDebug().WriteToLog() worker := &tcpWorker{ address: address, @@ -173,7 +167,7 @@ func (h *AlwaysOnInboundHandler) Close() error { } errs = append(errs, h.mux.Close()) if err := errors.Combine(errs...); err != nil { - return errors.New("failed to close all resources").Base(err) + return newError("failed to close all resources").Base(err) } return nil } @@ -193,16 +187,3 @@ func (h *AlwaysOnInboundHandler) Tag() string { func (h *AlwaysOnInboundHandler) GetInbound() proxy.Inbound { return h.proxy } - -// ReceiverSettings implements inbound.Handler. -func (h *AlwaysOnInboundHandler) ReceiverSettings() *serial.TypedMessage { - return serial.ToTypedMessage(h.receiverConfig) -} - -// ProxySettings implements inbound.Handler. -func (h *AlwaysOnInboundHandler) ProxySettings() *serial.TypedMessage { - if v, ok := h.proxyConfig.(proto.Message); ok { - return serial.ToTypedMessage(v) - } - return nil -} diff --git a/app/proxyman/inbound/dynamic.go b/app/proxyman/inbound/dynamic.go index f14a9952..0f81c26e 100644 --- a/app/proxyman/inbound/dynamic.go +++ b/app/proxyman/inbound/dynamic.go @@ -7,15 +7,12 @@ import ( "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 { @@ -25,7 +22,7 @@ type DynamicInboundHandler struct { receiverConfig *proxyman.ReceiverConfig streamSettings *internet.MemoryStreamConfig portMutex sync.Mutex - portsInUse map[net.Port]struct{} + portsInUse map[net.Port]bool workerMutex sync.RWMutex worker []worker lastRefresh time.Time @@ -41,7 +38,7 @@ func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *p tag: tag, proxyConfig: proxyConfig, receiverConfig: receiverConfig, - portsInUse: make(map[net.Port]struct{}), + portsInUse: make(map[net.Port]bool), mux: mux.NewServer(ctx), v: v, ctx: ctx, @@ -49,7 +46,7 @@ func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *p mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings) if err != nil { - return nil, errors.New("failed to parse stream settings").Base(err).AtWarning() + return nil, newError("failed to parse stream settings").Base(err).AtWarning() } if receiverConfig.ReceiveOriginalDestination { if mss.SocketSettings == nil { @@ -86,7 +83,7 @@ func (h *DynamicInboundHandler) allocatePort() net.Port { port := net.Port(allPorts[r]) _, used := h.portsInUse[port] if !used { - h.portsInUse[port] = struct{}{} + h.portsInUse[port] = true return port } } @@ -97,7 +94,7 @@ func (h *DynamicInboundHandler) closeWorkers(workers []worker) { for idx, worker := range workers { ports2Del[idx] = worker.Port() if err := worker.Close(); err != nil { - errors.LogInfoInner(h.ctx, err, "failed to close worker") + newError("failed to close worker").Base(err).WriteToLog() } } @@ -126,7 +123,7 @@ func (h *DynamicInboundHandler) refresh() error { port := h.allocatePort() rawProxy, err := core.CreateObject(h.v, h.proxyConfig) if err != nil { - errors.LogWarningInner(h.ctx, err, "failed to create proxy instance") + newError("failed to create proxy instance").Base(err).AtWarning().WriteToLog() continue } p := rawProxy.(proxy.Inbound) @@ -146,7 +143,7 @@ func (h *DynamicInboundHandler) refresh() error { ctx: h.ctx, } if err := worker.Start(); err != nil { - errors.LogWarningInner(h.ctx, err, "failed to create TCP worker") + newError("failed to create TCP worker").Base(err).AtWarning().WriteToLog() continue } workers = append(workers, worker) @@ -166,7 +163,7 @@ func (h *DynamicInboundHandler) refresh() error { ctx: h.ctx, } if err := worker.Start(); err != nil { - errors.LogWarningInner(h.ctx, err, "failed to create UDP worker") + newError("failed to create UDP worker").Base(err).AtWarning().WriteToLog() continue } workers = append(workers, worker) @@ -207,16 +204,3 @@ func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, 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 -} diff --git a/app/proxyman/inbound/errors.generated.go b/app/proxyman/inbound/errors.generated.go new file mode 100644 index 00000000..c2d7295e --- /dev/null +++ b/app/proxyman/inbound/errors.generated.go @@ -0,0 +1,9 @@ +package inbound + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index 5d64c5b2..3c9fb467 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -1,20 +1,20 @@ package inbound +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "sync" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" "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/features/inbound" ) -// Manager manages all inbound handlers. +// Manager is to manage all inbound handlers. type Manager struct { access sync.RWMutex untaggedHandler []inbound.Handler @@ -43,7 +43,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler inbound.Handler) error tag := handler.Tag() if len(tag) > 0 { if _, found := m.taggedHandlers[tag]; found { - return errors.New("existing tag found: " + tag) + return newError("existing tag found: " + tag) } m.taggedHandlers[tag] = handler } else { @@ -64,7 +64,7 @@ func (m *Manager) GetHandler(ctx context.Context, tag string) (inbound.Handler, handler, found := m.taggedHandlers[tag] if !found { - return nil, errors.New("handler not found: ", tag) + return nil, newError("handler not found: ", tag) } return handler, nil } @@ -80,7 +80,7 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { if handler, found := m.taggedHandlers[tag]; found { if err := handler.Close(); err != nil { - errors.LogWarningInner(ctx, err, "failed to close handler ", tag) + newError("failed to close handler ", tag).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } delete(m.taggedHandlers, tag) return nil @@ -89,21 +89,6 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { return common.ErrNoClue } -// ListHandlers implements inbound.Manager. -func (m *Manager) ListHandlers(ctx context.Context) []inbound.Handler { - m.access.RLock() - defer m.access.RUnlock() - - var response []inbound.Handler - copy(m.untaggedHandler, response) - - for _, v := range m.taggedHandlers { - response = append(response, v) - } - - return response -} - // Start implements common.Runnable. func (m *Manager) Start() error { m.access.Lock() @@ -132,20 +117,20 @@ func (m *Manager) Close() error { m.running = false - var errs []interface{} + var errors []interface{} for _, handler := range m.taggedHandlers { if err := handler.Close(); err != nil { - errs = append(errs, err) + errors = append(errors, err) } } for _, handler := range m.untaggedHandler { if err := handler.Close(); err != nil { - errs = append(errs, err) + errors = append(errors, err) } } - if len(errs) > 0 { - return errors.New("failed to close all handlers").Base(errors.New(serial.Concat(errs...))) + if len(errors) > 0 { + return newError("failed to close all handlers").Base(newError(serial.Concat(errors...))) } return nil @@ -165,7 +150,7 @@ func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound receiverSettings, ok := rawReceiverSettings.(*proxyman.ReceiverConfig) if !ok { - return nil, errors.New("not a ReceiverConfig").AtError() + return nil, newError("not a ReceiverConfig").AtError() } streamSettings := receiverSettings.StreamSettings @@ -174,9 +159,6 @@ func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound Mark: streamSettings.SocketSettings.Mark, }) } - if streamSettings != nil && streamSettings.ProtocolName == "splithttp" { - ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP) - } allocStrategy := receiverSettings.AllocationStrategy if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always { @@ -186,7 +168,7 @@ func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound if allocStrategy.Type == proxyman.AllocationStrategy_Random { return NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings) } - return nil, errors.New("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError() + return nil, newError("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError() } func init() { diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 12e29876..5b227c06 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -9,8 +9,6 @@ import ( "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - c "github.com/xtls/xray-core/common/ctx" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/session" @@ -60,16 +58,15 @@ func getTProxyType(s *internet.MemoryStreamConfig) internet.SocketConfig_TProxyM func (w *tcpWorker) callback(conn stat.Connection) { ctx, cancel := context.WithCancel(w.ctx) sid := session.NewID() - ctx = c.ContextWithID(ctx, sid) + ctx = session.ContextWithID(ctx, sid) - outbounds := []*session.Outbound{{}} if w.recvOrigDest { var dest net.Destination switch getTProxyType(w.stream) { case internet.SocketConfig_Redirect: d, err := tcp.GetOriginalDestination(conn) if err != nil { - errors.LogInfoInner(ctx, err, "failed to get original destination") + newError("failed to get original destination").Base(err).WriteToLog(session.ExportIDToError(ctx)) } else { dest = d } @@ -77,10 +74,11 @@ func (w *tcpWorker) callback(conn stat.Connection) { dest = net.DestinationFromAddr(conn.LocalAddr()) } if dest.IsValid() { - outbounds[0].Target = dest + ctx = session.ContextWithOutbound(ctx, &session.Outbound{ + Target: dest, + }) } } - ctx = session.ContextWithOutbounds(ctx, outbounds) if w.uplinkCounter != nil || w.downlinkCounter != nil { conn = &stat.CounterConnection{ @@ -107,7 +105,7 @@ func (w *tcpWorker) callback(conn stat.Connection) { ctx = session.ContextWithContent(ctx, content) if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil { - errors.LogInfoInner(ctx, err, "connection ends") + newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } cancel() conn.Close() @@ -123,24 +121,24 @@ func (w *tcpWorker) Start() error { go w.callback(conn) }) if err != nil { - return errors.New("failed to listen TCP on ", w.port).AtWarning().Base(err) + return newError("failed to listen TCP on ", w.port).AtWarning().Base(err) } w.hub = hub return nil } func (w *tcpWorker) Close() error { - var errs []interface{} + var errors []interface{} if w.hub != nil { if err := common.Close(w.hub); err != nil { - errs = append(errs, err) + errors = append(errors, err) } if err := common.Close(w.proxy); err != nil { - errs = append(errs, err) + errors = append(errors, err) } } - if len(errs) > 0 { - return errors.New("failed to close all resources").Base(errors.New(serial.Concat(errs...))) + if len(errors) > 0 { + return newError("failed to close all resources").Base(newError(serial.Concat(errors...))) } return nil @@ -308,13 +306,13 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest go func() { ctx := w.ctx sid := session.NewID() - ctx = c.ContextWithID(ctx, sid) + ctx = session.ContextWithID(ctx, sid) - outbounds := []*session.Outbound{{}} if originalDest.IsValid() { - outbounds[0].Target = originalDest + ctx = session.ContextWithOutbound(ctx, &session.Outbound{ + Target: originalDest, + }) } - ctx = session.ContextWithOutbounds(ctx, outbounds) ctx = session.ContextWithInbound(ctx, &session.Inbound{ Source: source, Gateway: net.UDPDestination(w.address, w.port), @@ -324,13 +322,12 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest 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) if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil { - errors.LogInfoInner(ctx, err, "connection ends") + newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } conn.Close() // conn not removed by checker TODO may be lock worker here is better @@ -361,11 +358,11 @@ func (w *udpWorker) clean() error { defer w.Unlock() if len(w.activeConn) == 0 { - return errors.New("no more connections. stopping...") + return newError("no more connections. stopping...") } for addr, conn := range w.activeConn { - if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 2*60 { + if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 5*60 { // TODO Timeout too small if !conn.inactive { conn.setInactive() delete(w.activeConn, addr) @@ -405,26 +402,26 @@ func (w *udpWorker) Close() error { w.Lock() defer w.Unlock() - var errs []interface{} + var errors []interface{} if w.hub != nil { if err := w.hub.Close(); err != nil { - errs = append(errs, err) + errors = append(errors, err) } } if w.checker != nil { if err := w.checker.Close(); err != nil { - errs = append(errs, err) + errors = append(errors, err) } } if err := common.Close(w.proxy); err != nil { - errs = append(errs, err) + errors = append(errors, err) } - if len(errs) > 0 { - return errors.New("failed to close all resources").Base(errors.New(serial.Concat(errs...))) + if len(errors) > 0 { + return newError("failed to close all resources").Base(newError(serial.Concat(errors...))) } return nil } @@ -455,7 +452,7 @@ type dsWorker struct { func (w *dsWorker) callback(conn stat.Connection) { ctx, cancel := context.WithCancel(w.ctx) sid := session.NewID() - ctx = c.ContextWithID(ctx, sid) + ctx = session.ContextWithID(ctx, sid) if w.uplinkCounter != nil || w.downlinkCounter != nil { conn = &stat.CounterConnection{ @@ -482,11 +479,11 @@ func (w *dsWorker) callback(conn stat.Connection) { ctx = session.ContextWithContent(ctx, content) if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil { - errors.LogInfoInner(ctx, err, "connection ends") + newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } cancel() if err := conn.Close(); err != nil { - errors.LogInfoInner(ctx, err, "failed to close connection") + newError("failed to close connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } @@ -504,24 +501,24 @@ func (w *dsWorker) Start() error { go w.callback(conn) }) if err != nil { - return errors.New("failed to listen Unix Domain Socket on ", w.address).AtWarning().Base(err) + return newError("failed to listen Unix Domain Socket on ", w.address).AtWarning().Base(err) } w.hub = hub return nil } func (w *dsWorker) Close() error { - var errs []interface{} + var errors []interface{} if w.hub != nil { if err := common.Close(w.hub); err != nil { - errs = append(errs, err) + errors = append(errors, err) } if err := common.Close(w.proxy); err != nil { - errs = append(errs, err) + errors = append(errors, err) } } - if len(errs) > 0 { - return errors.New("failed to close all resources").Base(errors.New(serial.Concat(errs...))) + if len(errors) > 0 { + return newError("failed to close all resources").Base(newError(serial.Concat(errors...))) } return nil diff --git a/app/proxyman/outbound/errors.generated.go b/app/proxyman/outbound/errors.generated.go new file mode 100644 index 00000000..07966823 --- /dev/null +++ b/app/proxyman/outbound/errors.generated.go @@ -0,0 +1,9 @@ +package outbound + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index bc9c635b..42554b72 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -2,21 +2,15 @@ package outbound import ( "context" - "crypto/rand" - goerrors "errors" + "errors" "io" - "math/big" - gonet "net" "os" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/mux" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" - "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/features/outbound" @@ -28,7 +22,6 @@ import ( "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/pipe" - "google.golang.org/protobuf/proto" ) func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) { @@ -56,17 +49,14 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) return uplinkCounter, downlinkCounter } -// Handler implements outbound.Handler. +// Handler is an implements of outbound.Handler. type Handler struct { tag string senderSettings *proxyman.SenderConfig streamSettings *internet.MemoryStreamConfig - proxyConfig proto.Message proxy proxy.Outbound outboundManager outbound.Manager mux *mux.ClientManager - xudp *mux.ClientManager - udp443 string uplinkCounter stats.Counter downlinkCounter stats.Counter } @@ -92,11 +82,11 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou h.senderSettings = s mss, err := internet.ToMemoryStreamConfig(s.StreamSettings) if err != nil { - return nil, errors.New("failed to parse stream settings").Base(err).AtWarning() + return nil, newError("failed to parse stream settings").Base(err).AtWarning() } h.streamSettings = mss default: - return nil, errors.New("settings is not SenderConfig") + return nil, newError("settings is not SenderConfig") } } @@ -104,7 +94,6 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou if err != nil { return nil, err } - h.proxyConfig = proxyConfig rawProxyHandler, err := common.CreateObject(ctx, proxyConfig) if err != nil { @@ -113,54 +102,26 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou proxyHandler, ok := rawProxyHandler.(proxy.Outbound) if !ok { - return nil, errors.New("not an outbound handler") + return nil, newError("not an outbound handler") } if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil { - if config := h.senderSettings.MultiplexSettings; config.Enabled { - if config.Concurrency < 0 { - h.mux = &mux.ClientManager{Enabled: false} - } - if config.Concurrency == 0 { - config.Concurrency = 8 // same as before - } - if config.Concurrency > 0 { - h.mux = &mux.ClientManager{ - Enabled: true, - Picker: &mux.IncrementalWorkerPicker{ - Factory: &mux.DialingWorkerFactory{ - Proxy: proxyHandler, - Dialer: h, - Strategy: mux.ClientStrategy{ - MaxConcurrency: uint32(config.Concurrency), - MaxConnection: 128, - }, - }, + config := h.senderSettings.MultiplexSettings + if config.Concurrency < 1 || config.Concurrency > 1024 { + return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning() + } + h.mux = &mux.ClientManager{ + Enabled: h.senderSettings.MultiplexSettings.Enabled, + Picker: &mux.IncrementalWorkerPicker{ + Factory: &mux.DialingWorkerFactory{ + Proxy: proxyHandler, + Dialer: h, + Strategy: mux.ClientStrategy{ + MaxConcurrency: config.Concurrency, + MaxConnection: 128, }, - } - } - if config.XudpConcurrency < 0 { - h.xudp = &mux.ClientManager{Enabled: false} - } - if config.XudpConcurrency == 0 { - h.xudp = nil // same as before - } - if config.XudpConcurrency > 0 { - h.xudp = &mux.ClientManager{ - Enabled: true, - Picker: &mux.IncrementalWorkerPicker{ - Factory: &mux.DialingWorkerFactory{ - Proxy: proxyHandler, - Dialer: h, - Strategy: mux.ClientStrategy{ - MaxConcurrency: uint32(config.XudpConcurrency), - MaxConnection: 128, - }, - }, - }, - } - } - h.udp443 = config.XudpProxyUDP443 + }, + }, } } @@ -175,59 +136,31 @@ func (h *Handler) Tag() string { // Dispatch implements proxy.Outbound.Dispatch. func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - 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} - } - if h.mux != nil { - test := func(err error) { - if err != nil { - err := errors.New("failed to process mux outbound traffic").Base(err) - session.SubmitOutboundErrorToOriginator(ctx, err) - errors.LogInfo(ctx, err.Error()) - common.Interrupt(link.Writer) - } + if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) { + if err := h.mux.Dispatch(ctx, link); err != nil { + err := newError("failed to process mux outbound traffic").Base(err) + session.SubmitOutboundErrorToOriginator(ctx, err) + err.WriteToLog(session.ExportIDToError(ctx)) + common.Interrupt(link.Writer) } - if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 { - switch h.udp443 { - case "reject": - test(errors.New("XUDP rejected UDP/443 traffic").AtInfo()) - return - case "skip": - goto out - } - } - if h.xudp != nil && ob.Target.Network == net.Network_UDP { - if !h.xudp.Enabled { - goto out - } - test(h.xudp.Dispatch(ctx, link)) - return - } - if h.mux.Enabled { - test(h.mux.Dispatch(ctx, link)) - return - } - } -out: - err := h.proxy.Process(ctx, link, h) - if err != nil { - if goerrors.Is(err, io.EOF) || goerrors.Is(err, io.ErrClosedPipe) || goerrors.Is(err, context.Canceled) { - err = nil - } - } - if err != nil { - // Ensure outbound ray is properly closed. - err := errors.New("failed to process outbound traffic").Base(err) - session.SubmitOutboundErrorToOriginator(ctx, err) - errors.LogInfo(ctx, err.Error()) - common.Interrupt(link.Writer) } else { - common.Close(link.Writer) + err := h.proxy.Process(ctx, link, h) + if err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, context.Canceled) { + err = nil + } + } + if err != nil { + // Ensure outbound ray is properly closed. + err := newError("failed to process outbound traffic").Base(err) + session.SubmitOutboundErrorToOriginator(ctx, err) + err.WriteToLog(session.ExportIDToError(ctx)) + common.Interrupt(link.Writer) + } else { + common.Must(common.Close(link.Writer)) + } + common.Interrupt(link.Reader) } - common.Interrupt(link.Reader) } // Address implements internet.Dialer. @@ -238,25 +171,18 @@ func (h *Handler) Address() net.Address { return h.senderSettings.Via.AsAddress() } -func (h *Handler) DestIpAddress() net.IP { - return internet.DestIpAddress() -} - // Dial implements internet.Dialer. func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) { if h.senderSettings != nil { - if h.senderSettings.ProxySettings.HasTag() { - tag := h.senderSettings.ProxySettings.Tag handler := h.outboundManager.GetHandler(tag) if handler != nil { - errors.LogDebug(ctx, "proxying to ", tag, " for dest ", dest) - outbounds := session.OutboundsFromContext(ctx) - ctx = session.ContextWithOutbounds(ctx, append(outbounds, &session.Outbound{ + newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) + ctx = session.ContextWithOutbound(ctx, &session.Outbound{ Target: dest, - Tag: tag, - })) // add another outbound in session ctx + }) + opts := pipe.OptionsFromContext(ctx) uplinkReader, uplinkWriter := pipe.New(opts...) downlinkReader, downlinkWriter := pipe.New(opts...) @@ -272,48 +198,16 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti return h.getStatCouterConnection(conn), nil } - errors.LogWarning(ctx, "failed to get outbound handler with tag: ", tag) + newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } if h.senderSettings.Via != nil { - - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - var domain string - addr := h.senderSettings.Via.AsAddress() - domain = h.senderSettings.Via.GetDomain() - switch { - case h.senderSettings.ViaCidr != "": - ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr) - - case domain == "origin": - - if inbound := session.InboundFromContext(ctx); inbound != nil { - if inbound.Conn != nil { - origin, _, err := net.SplitHostPort(inbound.Conn.LocalAddr().String()) - if err == nil { - ob.Gateway = net.ParseAddress(origin) - errors.LogDebug(ctx, "use receive package ip as snedthrough: ", origin) - } - } - } - case domain == "srcip": - if inbound := session.InboundFromContext(ctx); inbound != nil { - if inbound.Conn != nil { - clientaddr, _, err := net.SplitHostPort(inbound.Conn.RemoteAddr().String()) - if err == nil { - ob.Gateway = net.ParseAddress(clientaddr) - errors.LogDebug(ctx, "use client src ip as snedthrough: ", clientaddr) - } - } - - } - //case addr.Family().IsDomain(): - default: - ob.Gateway = addr - + outbound := session.OutboundFromContext(ctx) + if outbound == nil { + outbound = new(session.Outbound) + ctx = session.ContextWithOutbound(ctx, outbound) } - + outbound.Gateway = h.senderSettings.Via.AsAddress() } } @@ -322,11 +216,7 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti } conn, err := internet.Dial(ctx, dest, h.streamSettings) - conn = h.getStatCouterConnection(conn) - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - ob.Conn = conn - return conn, err + return h.getStatCouterConnection(conn), err } func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection { @@ -353,35 +243,5 @@ func (h *Handler) Start() error { // Close implements common.Closable. func (h *Handler) Close() error { common.Close(h.mux) - common.Close(h.proxy) return nil } - -// SenderSettings implements outbound.Handler. -func (h *Handler) SenderSettings() *serial.TypedMessage { - return serial.ToTypedMessage(h.senderSettings) -} - -// ProxySettings implements outbound.Handler. -func (h *Handler) ProxySettings() *serial.TypedMessage { - return serial.ToTypedMessage(h.proxyConfig) -} - -func ParseRandomIP(addr net.Address, prefix string) net.Address { - - _, ipnet, _ := gonet.ParseCIDR(addr.IP().String() + "/" + prefix) - - ones, bits := ipnet.Mask.Size() - subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones)) - - rnd, _ := rand.Int(rand.Reader, subnetSize) - - startInt := new(big.Int).SetBytes(ipnet.IP) - rndInt := new(big.Int).Add(startInt, rnd) - - rndBytes := rndInt.Bytes() - padded := make([]byte, len(ipnet.IP)) - copy(padded[len(padded)-len(rndBytes):], rndBytes) - - return net.ParseAddress(gonet.IP(padded).String()) -} diff --git a/app/proxyman/outbound/handler_test.go b/app/proxyman/outbound/handler_test.go index 3f7ef28e..c5afea70 100644 --- a/app/proxyman/outbound/handler_test.go +++ b/app/proxyman/outbound/handler_test.go @@ -2,19 +2,13 @@ package outbound_test import ( "context" - "fmt" - "sync" - "sync/atomic" "testing" - "time" "github.com/xtls/xray-core/app/policy" - "github.com/xtls/xray-core/app/proxyman" . "github.com/xtls/xray-core/app/proxyman/outbound" "github.com/xtls/xray-core/app/stats" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/common/session" core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/proxy/freedom" @@ -45,7 +39,6 @@ func TestOutboundWithoutStatCounter(t *testing.T) { v, _ := core.New(config) v.AddFeature((outbound.Manager)(new(Manager))) ctx := context.WithValue(context.Background(), xrayKey, v) - ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}}) h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ Tag: "tag", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), @@ -75,7 +68,6 @@ func TestOutboundWithStatCounter(t *testing.T) { v, _ := core.New(config) v.AddFeature((outbound.Manager)(new(Manager))) ctx := context.WithValue(context.Background(), xrayKey, v) - ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}}) h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ Tag: "tag", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), @@ -86,91 +78,3 @@ func TestOutboundWithStatCounter(t *testing.T) { t.Errorf("Expected conn to be CounterConnection") } } - -func TestTagsCache(t *testing.T) { - - test_duration := 10 * time.Second - threads_num := 50 - delay := 10 * time.Millisecond - tags_prefix := "node" - - tags := sync.Map{} - counter := atomic.Uint64{} - - ohm, err := New(context.Background(), &proxyman.OutboundConfig{}) - if err != nil { - t.Error("failed to create outbound handler manager") - } - config := &core.Config{ - App: []*serial.TypedMessage{}, - } - v, _ := core.New(config) - v.AddFeature(ohm) - ctx := context.WithValue(context.Background(), xrayKey, v) - - stop_add_rm := false - wg_add_rm := sync.WaitGroup{} - addHandlers := func() { - defer wg_add_rm.Done() - for !stop_add_rm { - time.Sleep(delay) - idx := counter.Add(1) - tag := fmt.Sprintf("%s%d", tags_prefix, idx) - cfg := &core.OutboundHandlerConfig{ - Tag: tag, - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - } - if h, err := NewHandler(ctx, cfg); err == nil { - if err := ohm.AddHandler(ctx, h); err == nil { - // t.Log("add handler:", tag) - tags.Store(tag, nil) - } else { - t.Error("failed to add handler:", tag) - } - } else { - t.Error("failed to create handler:", tag) - } - } - } - - rmHandlers := func() { - defer wg_add_rm.Done() - for !stop_add_rm { - time.Sleep(delay) - tags.Range(func(key interface{}, value interface{}) bool { - if _, ok := tags.LoadAndDelete(key); ok { - // t.Log("remove handler:", key) - ohm.RemoveHandler(ctx, key.(string)) - return false - } - return true - }) - } - } - - selectors := []string{tags_prefix} - wg_get := sync.WaitGroup{} - stop_get := false - getTags := func() { - defer wg_get.Done() - for !stop_get { - time.Sleep(delay) - _ = ohm.Select(selectors) - // t.Logf("get tags: %v", tag) - } - } - - for i := 0; i < threads_num; i++ { - wg_add_rm.Add(2) - go rmHandlers() - go addHandlers() - wg_get.Add(1) - go getTags() - } - - time.Sleep(test_duration) - stop_add_rm = true - wg_add_rm.Wait() - stop_get = true - wg_get.Wait() -} diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index ed47b778..8ebcde17 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -1,8 +1,9 @@ package outbound +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - "sort" "strings" "sync" @@ -20,14 +21,12 @@ type Manager struct { taggedHandler map[string]outbound.Handler untaggedHandlers []outbound.Handler running bool - tagsCache *sync.Map } // New creates a new Manager. func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) { m := &Manager{ taggedHandler: make(map[string]outbound.Handler), - tagsCache: &sync.Map{}, } return m, nil } @@ -104,8 +103,6 @@ func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) erro m.access.Lock() defer m.access.Unlock() - m.tagsCache = &sync.Map{} - if m.defaultHandler == nil { m.defaultHandler = handler } @@ -113,7 +110,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) erro tag := handler.Tag() if len(tag) > 0 { if _, found := m.taggedHandler[tag]; found { - return errors.New("existing tag found: " + tag) + return newError("existing tag found: " + tag) } m.taggedHandler[tag] = handler } else { @@ -135,8 +132,6 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { m.access.Lock() defer m.access.Unlock() - m.tagsCache = &sync.Map{} - delete(m.taggedHandler, tag) if m.defaultHandler != nil && m.defaultHandler.Tag() == tag { m.defaultHandler = nil @@ -145,46 +140,26 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { return nil } -// ListHandlers implements outbound.Manager. -func (m *Manager) ListHandlers(ctx context.Context) []outbound.Handler { - m.access.RLock() - defer m.access.RUnlock() - - var response []outbound.Handler - copy(m.untaggedHandlers, response) - - for _, v := range m.taggedHandler { - response = append(response, v) - } - - return response -} - // Select implements outbound.HandlerSelector. func (m *Manager) Select(selectors []string) []string { - - key := strings.Join(selectors, ",") - if cache, ok := m.tagsCache.Load(key); ok { - return cache.([]string) - } - m.access.RLock() defer m.access.RUnlock() tags := make([]string, 0, len(selectors)) for tag := range m.taggedHandler { + match := false for _, selector := range selectors { if strings.HasPrefix(tag, selector) { - tags = append(tags, tag) + match = true break } } + if match { + tags = append(tags, tag) + } } - sort.Strings(tags) - m.tagsCache.Store(key, tags) - return tags } diff --git a/app/proxyman/outbound/uot.go b/app/proxyman/outbound/uot.go index 659f65a1..a4af220c 100644 --- a/app/proxyman/outbound/uot.go +++ b/app/proxyman/outbound/uot.go @@ -5,31 +5,19 @@ import ( "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 { + if !dest.Address.Family().IsDomain() || dest.Address.Domain() != uot.UOTMagicAddress { 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) + return nil, newError("unable to listen socket").Base(err) } - conn := uot.NewServerConn(packetConn, uotVersion) + conn := uot.NewServerConn(packetConn) return h.getStatCouterConnection(conn), nil } diff --git a/app/reverse/bridge.go b/app/reverse/bridge.go index 3e46cc6c..4b86c3a2 100644 --- a/app/reverse/bridge.go +++ b/app/reverse/bridge.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/mux" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" @@ -12,7 +12,6 @@ import ( "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/pipe" - "google.golang.org/protobuf/proto" ) // Bridge is a component in reverse proxy, that relays connections from Portal to local address. @@ -27,10 +26,10 @@ type Bridge struct { // NewBridge creates a new Bridge instance. func NewBridge(config *BridgeConfig, dispatcher routing.Dispatcher) (*Bridge, error) { if config.Tag == "" { - return nil, errors.New("bridge tag is empty") + return nil, newError("bridge tag is empty") } if config.Domain == "" { - return nil, errors.New("bridge domain is empty") + return nil, newError("bridge domain is empty") } b := &Bridge{ @@ -75,7 +74,7 @@ func (b *Bridge) monitor() error { if numWorker == 0 || numConnections/numWorker > 16 { worker, err := NewBridgeWorker(b.domain, b.tag, b.dispatcher) if err != nil { - errors.LogWarningInner(context.Background(), err, "failed to create bridge worker") + newError("failed to create bridge worker").Base(err).AtWarning().WriteToLog() return nil } b.workers = append(b.workers, worker) @@ -158,7 +157,7 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) { for _, b := range mb { var ctl Control if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil { - errors.LogInfoInner(context.Background(), err, "failed to parse proto message") + newError("failed to parse proto message").Base(err).WriteToLog() break } if ctl.State != w.state { diff --git a/app/reverse/config.pb.go b/app/reverse/config.pb.go index e9865d86..c5478280 100644 --- a/app/reverse/config.pb.go +++ b/app/reverse/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/reverse/config.proto package reverse @@ -77,9 +77,11 @@ type Control struct { func (x *Control) Reset() { *x = Control{} - mi := &file_app_reverse_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Control) String() string { @@ -90,7 +92,7 @@ func (*Control) ProtoMessage() {} func (x *Control) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -130,9 +132,11 @@ type BridgeConfig struct { func (x *BridgeConfig) Reset() { *x = BridgeConfig{} - mi := &file_app_reverse_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *BridgeConfig) String() string { @@ -143,7 +147,7 @@ func (*BridgeConfig) ProtoMessage() {} func (x *BridgeConfig) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -183,9 +187,11 @@ type PortalConfig struct { func (x *PortalConfig) Reset() { *x = PortalConfig{} - mi := &file_app_reverse_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *PortalConfig) String() string { @@ -196,7 +202,7 @@ func (*PortalConfig) ProtoMessage() {} func (x *PortalConfig) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -236,9 +242,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_reverse_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_reverse_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -249,7 +257,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -330,7 +338,7 @@ func file_app_reverse_config_proto_rawDescGZIP() []byte { var file_app_reverse_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_reverse_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_app_reverse_config_proto_goTypes = []any{ +var file_app_reverse_config_proto_goTypes = []interface{}{ (Control_State)(0), // 0: xray.app.reverse.Control.State (*Control)(nil), // 1: xray.app.reverse.Control (*BridgeConfig)(nil), // 2: xray.app.reverse.BridgeConfig @@ -353,6 +361,56 @@ func file_app_reverse_config_proto_init() { if File_app_reverse_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_reverse_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Control); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BridgeConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PortalConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_reverse_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/reverse/errors.generated.go b/app/reverse/errors.generated.go new file mode 100644 index 00000000..5dc907a2 --- /dev/null +++ b/app/reverse/errors.generated.go @@ -0,0 +1,9 @@ +package reverse + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/reverse/portal.go b/app/reverse/portal.go index 818c5718..b0860a6e 100644 --- a/app/reverse/portal.go +++ b/app/reverse/portal.go @@ -5,18 +5,16 @@ import ( "sync" "time" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/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/session" "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/pipe" - "google.golang.org/protobuf/proto" ) type Portal struct { @@ -29,11 +27,11 @@ type Portal struct { func NewPortal(config *PortalConfig, ohm outbound.Manager) (*Portal, error) { if config.Tag == "" { - return nil, errors.New("portal tag is empty") + return nil, newError("portal tag is empty") } if config.Domain == "" { - return nil, errors.New("portal domain is empty") + return nil, newError("portal domain is empty") } picker, err := NewStaticMuxPicker() @@ -64,21 +62,20 @@ func (p *Portal) Close() error { } func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if ob == nil { - return errors.New("outbound metadata not found").AtError() + outboundMeta := session.OutboundFromContext(ctx) + if outboundMeta == nil { + return newError("outbound metadata not found").AtError() } - if isDomain(ob.Target, p.domain) { + if isDomain(outboundMeta.Target, p.domain) { muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{}) if err != nil { - return errors.New("failed to create mux client worker").Base(err).AtWarning() + return newError("failed to create mux client worker").Base(err).AtWarning() } worker, err := NewPortalWorker(muxClient) if err != nil { - return errors.New("failed to create portal worker").Base(err) + return newError("failed to create portal worker").Base(err) } p.picker.AddWorker(worker) @@ -99,7 +96,7 @@ func (o *Outbound) Tag() string { func (o *Outbound) Dispatch(ctx context.Context, link *transport.Link) { if err := o.portal.HandleConnection(ctx, link); err != nil { - errors.LogInfoInner(ctx, err, "failed to process reverse connection") + newError("failed to process reverse connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) common.Interrupt(link.Writer) } } @@ -112,16 +109,6 @@ func (o *Outbound) Close() error { return nil } -// SenderSettings implements outbound.Handler. -func (o *Outbound) SenderSettings() *serial.TypedMessage { - return nil -} - -// ProxySettings implements outbound.Handler. -func (o *Outbound) ProxySettings() *serial.TypedMessage { - return nil -} - type StaticMuxPicker struct { access sync.Mutex workers []*PortalWorker @@ -161,7 +148,7 @@ func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) { defer p.access.Unlock() if len(p.workers) == 0 { - return nil, errors.New("empty worker list") + return nil, newError("empty worker list") } var minIdx int = -1 @@ -195,7 +182,7 @@ func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) { return p.workers[minIdx].client, nil } - return nil, errors.New("no mux client worker available") + return nil, newError("no mux client worker available") } func (p *StaticMuxPicker) AddWorker(worker *PortalWorker) { @@ -219,16 +206,15 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) { downlinkReader, downlinkWriter := pipe.New(opt...) ctx := context.Background() - outbounds := []*session.Outbound{{ + ctx = session.ContextWithOutbound(ctx, &session.Outbound{ Target: net.UDPDestination(net.DomainAddress(internalDomain), 0), - }} - ctx = session.ContextWithOutbounds(ctx, outbounds) + }) f := client.Dispatch(ctx, &transport.Link{ Reader: uplinkReader, Writer: downlinkWriter, }) if !f { - return nil, errors.New("unable to dispatch control connection") + return nil, newError("unable to dispatch control connection") } w := &PortalWorker{ client: client, @@ -245,11 +231,11 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) { func (w *PortalWorker) heartbeat() error { if w.client.Closed() { - return errors.New("client worker stopped") + return newError("client worker stopped") } if w.draining || w.writer == nil { - return errors.New("already disposed") + return newError("already disposed") } msg := &Control{} diff --git a/app/reverse/reverse.go b/app/reverse/reverse.go index f550a23f..077a89c1 100644 --- a/app/reverse/reverse.go +++ b/app/reverse/reverse.go @@ -1,5 +1,7 @@ package reverse +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" diff --git a/app/router/balancing.go b/app/router/balancing.go index 5f8cb1c2..50b84388 100644 --- a/app/router/balancing.go +++ b/app/router/balancing.go @@ -2,12 +2,8 @@ package router import ( "context" - sync "sync" - "github.com/xtls/xray-core/app/observatory" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/features/extension" "github.com/xtls/xray-core/features/outbound" ) @@ -16,104 +12,35 @@ type BalancingStrategy interface { PickOutbound([]string) string } -type BalancingPrincipleTarget interface { - GetPrincipleTarget([]string) []string -} - -type RoundRobinStrategy struct { - FallbackTag string - - ctx context.Context - observatory extension.Observatory - mu sync.Mutex - index int -} - -func (s *RoundRobinStrategy) InjectContext(ctx context.Context) { - s.ctx = ctx - if len(s.FallbackTag) > 0 { - common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error { - s.observatory = observatory - return nil - })) - } -} - -func (s *RoundRobinStrategy) GetPrincipleTarget(strings []string) []string { - return strings -} - -func (s *RoundRobinStrategy) PickOutbound(tags []string) string { - if s.observatory != nil { - observeReport, err := s.observatory.GetObservation(s.ctx) - if err == nil { - aliveTags := make([]string, 0) - if result, ok := observeReport.(*observatory.ObservationResult); ok { - status := result.Status - statusMap := make(map[string]*observatory.OutboundStatus) - for _, outboundStatus := range status { - statusMap[outboundStatus.OutboundTag] = outboundStatus - } - for _, candidate := range tags { - if outboundStatus, found := statusMap[candidate]; found { - if outboundStatus.Alive { - aliveTags = append(aliveTags, candidate) - } - } else { - // unfound candidate is considered alive - aliveTags = append(aliveTags, candidate) - } - } - tags = aliveTags - } - } - } +type RandomStrategy struct{} +func (s *RandomStrategy) PickOutbound(tags []string) string { n := len(tags) if n == 0 { - // goes to fallbackTag - return "" + panic("0 tags") } - s.mu.Lock() - defer s.mu.Unlock() - tag := tags[s.index%n] - s.index = (s.index + 1) % n - return tag + return tags[dice.Roll(n)] } type Balancer struct { - selectors []string - strategy BalancingStrategy - ohm outbound.Manager - fallbackTag string - - override override + selectors []string + strategy BalancingStrategy + ohm outbound.Manager } -// PickOutbound picks the tag of a outbound func (b *Balancer) PickOutbound() (string, error) { - candidates, err := b.SelectOutbounds() - if err != nil { - if b.fallbackTag != "" { - errors.LogInfo(context.Background(), "fallback to [", b.fallbackTag, "], due to error: ", err) - return b.fallbackTag, nil - } - return "", err + hs, ok := b.ohm.(outbound.HandlerSelector) + if !ok { + return "", newError("outbound.Manager is not a HandlerSelector") } - var tag string - if o := b.override.Get(); o != "" { - tag = o - } else { - tag = b.strategy.PickOutbound(candidates) + tags := hs.Select(b.selectors) + if len(tags) == 0 { + return "", newError("no available outbounds selected") } + tag := b.strategy.PickOutbound(tags) if tag == "" { - if b.fallbackTag != "" { - errors.LogInfo(context.Background(), "fallback to [", b.fallbackTag, "], due to empty tag returned") - return b.fallbackTag, nil - } - // will use default handler - return "", errors.New("balancing strategy returns empty tag") + return "", newError("balancing strategy returns empty tag") } return tag, nil } @@ -123,45 +50,3 @@ func (b *Balancer) InjectContext(ctx context.Context) { contextReceiver.InjectContext(ctx) } } - -// SelectOutbounds select outbounds with selectors of the Balancer -func (b *Balancer) SelectOutbounds() ([]string, error) { - hs, ok := b.ohm.(outbound.HandlerSelector) - if !ok { - return nil, errors.New("outbound.Manager is not a HandlerSelector") - } - tags := hs.Select(b.selectors) - return tags, nil -} - -// GetPrincipleTarget implements routing.BalancerPrincipleTarget -func (r *Router) GetPrincipleTarget(tag string) ([]string, error) { - if b, ok := r.balancers[tag]; ok { - if s, ok := b.strategy.(BalancingPrincipleTarget); ok { - candidates, err := b.SelectOutbounds() - if err != nil { - return nil, errors.New("unable to select outbounds").Base(err) - } - return s.GetPrincipleTarget(candidates), nil - } - return nil, errors.New("unsupported GetPrincipleTarget") - } - return nil, errors.New("cannot find tag") -} - -// SetOverrideTarget implements routing.BalancerOverrider -func (r *Router) SetOverrideTarget(tag, target string) error { - if b, ok := r.balancers[tag]; ok { - b.override.Put(target) - return nil - } - return errors.New("cannot find tag") -} - -// GetOverrideTarget implements routing.BalancerOverrider -func (r *Router) GetOverrideTarget(tag string) (string, error) { - if b, ok := r.balancers[tag]; ok { - return b.override.Get(), nil - } - return "", errors.New("cannot find tag") -} diff --git a/app/router/balancing_override.go b/app/router/balancing_override.go deleted file mode 100644 index 96c0aea1..00000000 --- a/app/router/balancing_override.go +++ /dev/null @@ -1,52 +0,0 @@ -package router - -import ( - sync "sync" - - "github.com/xtls/xray-core/common/errors" -) - -func (r *Router) OverrideBalancer(balancer string, target string) error { - var b *Balancer - for tag, bl := range r.balancers { - if tag == balancer { - b = bl - break - } - } - if b == nil { - return errors.New("balancer '", balancer, "' not found") - } - b.override.Put(target) - return nil -} - -type overrideSettings struct { - target string -} - -type override struct { - access sync.RWMutex - settings overrideSettings -} - -// Get gets the override settings -func (o *override) Get() string { - o.access.RLock() - defer o.access.RUnlock() - return o.settings.target -} - -// Put updates the override settings -func (o *override) Put(target string) { - o.access.Lock() - defer o.access.Unlock() - o.settings.target = target -} - -// Clear clears the override settings -func (o *override) Clear() { - o.access.Lock() - defer o.access.Unlock() - o.settings.target = "" -} diff --git a/app/router/command/command.go b/app/router/command/command.go index fd9caa22..58981056 100644 --- a/app/router/command/command.go +++ b/app/router/command/command.go @@ -1,11 +1,12 @@ package command +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "time" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/stats" @@ -18,55 +19,6 @@ type routingServer struct { routingStats stats.Channel } -func (s *routingServer) GetBalancerInfo(ctx context.Context, request *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) { - var ret GetBalancerInfoResponse - ret.Balancer = &BalancerMsg{} - if bo, ok := s.router.(routing.BalancerOverrider); ok { - { - res, err := bo.GetOverrideTarget(request.GetTag()) - if err != nil { - return nil, err - } - ret.Balancer.Override = &OverrideInfo{ - Target: res, - } - } - } - - if pt, ok := s.router.(routing.BalancerPrincipleTarget); ok { - { - res, err := pt.GetPrincipleTarget(request.GetTag()) - if err != nil { - errors.LogInfoInner(ctx, err, "unable to obtain principle target") - } else { - ret.Balancer.PrincipleTarget = &PrincipleTargetInfo{Tag: res} - } - } - } - return &ret, nil -} - -func (s *routingServer) OverrideBalancerTarget(ctx context.Context, request *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) { - if bo, ok := s.router.(routing.BalancerOverrider); ok { - return &OverrideBalancerTargetResponse{}, bo.SetOverrideTarget(request.BalancerTag, request.Target) - } - return nil, errors.New("unsupported router implementation") -} - -func (s *routingServer) AddRule(ctx context.Context, request *AddRuleRequest) (*AddRuleResponse, error) { - if bo, ok := s.router.(routing.Router); ok { - return &AddRuleResponse{}, bo.AddRule(request.Config, request.ShouldAppend) - } - return nil, errors.New("unsupported router implementation") - -} -func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) { - if bo, ok := s.router.(routing.Router); ok { - return &RemoveRuleResponse{}, bo.RemoveRule(request.RuleTag) - } - return nil, errors.New("unsupported router implementation") -} - // NewRoutingServer creates a statistics service with statistics manager. func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer { return &routingServer{ @@ -77,7 +29,7 @@ func NewRoutingServer(router routing.Router, routingStats stats.Channel) Routing func (s *routingServer) TestRoute(ctx context.Context, request *TestRouteRequest) (*RoutingContext, error) { if request.RoutingContext == nil { - return nil, errors.New("Invalid routing request.") + return nil, newError("Invalid routing request.") } route, err := s.router.PickRoute(AsRoutingContext(request.RoutingContext)) if err != nil { @@ -92,7 +44,7 @@ func (s *routingServer) TestRoute(ctx context.Context, request *TestRouteRequest func (s *routingServer) SubscribeRoutingStats(request *SubscribeRoutingStatsRequest, stream RoutingService_SubscribeRoutingStatsServer) error { if s.routingStats == nil { - return errors.New("Routing statistics not enabled.") + return newError("Routing statistics not enabled.") } genMessage := AsProtobufMessage(request.FieldSelectors) subscriber, err := stats.SubscribeRunnableChannel(s.routingStats) @@ -104,11 +56,11 @@ func (s *routingServer) SubscribeRoutingStats(request *SubscribeRoutingStatsRequ select { case value, ok := <-subscriber: if !ok { - return errors.New("Upstream closed the subscriber channel.") + return newError("Upstream closed the subscriber channel.") } route, ok := value.(routing.Route) if !ok { - return errors.New("Upstream sent malformed statistics.") + return newError("Upstream sent malformed statistics.") } err := stream.Send(genMessage(route)) if err != nil { @@ -135,7 +87,7 @@ func (s *service) Register(server *grpc.Server) { vCoreDesc := RoutingService_ServiceDesc vCoreDesc.ServiceName = "v2ray.core.app.router.command.RoutingService" server.RegisterService(&vCoreDesc, rs) - }, false)) + })) } func init() { diff --git a/app/router/command/command.pb.go b/app/router/command/command.pb.go index 6e388901..41c52388 100644 --- a/app/router/command/command.pb.go +++ b/app/router/command/command.pb.go @@ -1,14 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/router/command/command.proto package command import ( net "github.com/xtls/xray-core/common/net" - serial "github.com/xtls/xray-core/common/serial" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -46,9 +45,11 @@ type RoutingContext struct { func (x *RoutingContext) Reset() { *x = RoutingContext{} - mi := &file_app_router_command_command_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_command_command_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RoutingContext) String() string { @@ -59,7 +60,7 @@ func (*RoutingContext) ProtoMessage() {} func (x *RoutingContext) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -186,9 +187,11 @@ type SubscribeRoutingStatsRequest struct { func (x *SubscribeRoutingStatsRequest) Reset() { *x = SubscribeRoutingStatsRequest{} - mi := &file_app_router_command_command_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_command_command_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SubscribeRoutingStatsRequest) String() string { @@ -199,7 +202,7 @@ func (*SubscribeRoutingStatsRequest) ProtoMessage() {} func (x *SubscribeRoutingStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -240,9 +243,11 @@ type TestRouteRequest struct { func (x *TestRouteRequest) Reset() { *x = TestRouteRequest{} - mi := &file_app_router_command_command_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_command_command_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *TestRouteRequest) String() string { @@ -253,7 +258,7 @@ func (*TestRouteRequest) ProtoMessage() {} func (x *TestRouteRequest) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -289,498 +294,6 @@ func (x *TestRouteRequest) GetPublishResult() bool { return false } -type PrincipleTargetInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"` -} - -func (x *PrincipleTargetInfo) Reset() { - *x = PrincipleTargetInfo{} - mi := &file_app_router_command_command_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PrincipleTargetInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PrincipleTargetInfo) ProtoMessage() {} - -func (x *PrincipleTargetInfo) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_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 PrincipleTargetInfo.ProtoReflect.Descriptor instead. -func (*PrincipleTargetInfo) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{3} -} - -func (x *PrincipleTargetInfo) GetTag() []string { - if x != nil { - return x.Tag - } - return nil -} - -type OverrideInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` -} - -func (x *OverrideInfo) Reset() { - *x = OverrideInfo{} - mi := &file_app_router_command_command_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *OverrideInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OverrideInfo) ProtoMessage() {} - -func (x *OverrideInfo) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OverrideInfo.ProtoReflect.Descriptor instead. -func (*OverrideInfo) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{4} -} - -func (x *OverrideInfo) GetTarget() string { - if x != nil { - return x.Target - } - return "" -} - -type BalancerMsg struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - 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() { - *x = BalancerMsg{} - mi := &file_app_router_command_command_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BalancerMsg) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BalancerMsg) ProtoMessage() {} - -func (x *BalancerMsg) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[5] - 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 BalancerMsg.ProtoReflect.Descriptor instead. -func (*BalancerMsg) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{5} -} - -func (x *BalancerMsg) GetOverride() *OverrideInfo { - if x != nil { - return x.Override - } - return nil -} - -func (x *BalancerMsg) GetPrincipleTarget() *PrincipleTargetInfo { - if x != nil { - return x.PrincipleTarget - } - return nil -} - -type GetBalancerInfoRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` -} - -func (x *GetBalancerInfoRequest) Reset() { - *x = GetBalancerInfoRequest{} - mi := &file_app_router_command_command_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetBalancerInfoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBalancerInfoRequest) ProtoMessage() {} - -func (x *GetBalancerInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[6] - 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 GetBalancerInfoRequest.ProtoReflect.Descriptor instead. -func (*GetBalancerInfoRequest) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{6} -} - -func (x *GetBalancerInfoRequest) GetTag() string { - if x != nil { - return x.Tag - } - return "" -} - -type GetBalancerInfoResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"` -} - -func (x *GetBalancerInfoResponse) Reset() { - *x = GetBalancerInfoResponse{} - mi := &file_app_router_command_command_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetBalancerInfoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBalancerInfoResponse) ProtoMessage() {} - -func (x *GetBalancerInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetBalancerInfoResponse.ProtoReflect.Descriptor instead. -func (*GetBalancerInfoResponse) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{7} -} - -func (x *GetBalancerInfoResponse) GetBalancer() *BalancerMsg { - if x != nil { - return x.Balancer - } - return nil -} - -type OverrideBalancerTargetRequest struct { - state protoimpl.MessageState - 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() { - *x = OverrideBalancerTargetRequest{} - mi := &file_app_router_command_command_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *OverrideBalancerTargetRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OverrideBalancerTargetRequest) ProtoMessage() {} - -func (x *OverrideBalancerTargetRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_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 OverrideBalancerTargetRequest.ProtoReflect.Descriptor instead. -func (*OverrideBalancerTargetRequest) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{8} -} - -func (x *OverrideBalancerTargetRequest) GetBalancerTag() string { - if x != nil { - return x.BalancerTag - } - return "" -} - -func (x *OverrideBalancerTargetRequest) GetTarget() string { - if x != nil { - return x.Target - } - return "" -} - -type OverrideBalancerTargetResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *OverrideBalancerTargetResponse) Reset() { - *x = OverrideBalancerTargetResponse{} - mi := &file_app_router_command_command_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *OverrideBalancerTargetResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OverrideBalancerTargetResponse) ProtoMessage() {} - -func (x *OverrideBalancerTargetResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_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 OverrideBalancerTargetResponse.ProtoReflect.Descriptor instead. -func (*OverrideBalancerTargetResponse) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{9} -} - -type AddRuleRequest struct { - state protoimpl.MessageState - 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() { - *x = AddRuleRequest{} - mi := &file_app_router_command_command_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddRuleRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddRuleRequest) ProtoMessage() {} - -func (x *AddRuleRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[10] - 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 AddRuleRequest.ProtoReflect.Descriptor instead. -func (*AddRuleRequest) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{10} -} - -func (x *AddRuleRequest) GetConfig() *serial.TypedMessage { - if x != nil { - return x.Config - } - return nil -} - -func (x *AddRuleRequest) GetShouldAppend() bool { - if x != nil { - return x.ShouldAppend - } - return false -} - -type AddRuleResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *AddRuleResponse) Reset() { - *x = AddRuleResponse{} - mi := &file_app_router_command_command_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddRuleResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddRuleResponse) ProtoMessage() {} - -func (x *AddRuleResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[11] - 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 AddRuleResponse.ProtoReflect.Descriptor instead. -func (*AddRuleResponse) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{11} -} - -type RemoveRuleRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"` -} - -func (x *RemoveRuleRequest) Reset() { - *x = RemoveRuleRequest{} - mi := &file_app_router_command_command_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveRuleRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveRuleRequest) ProtoMessage() {} - -func (x *RemoveRuleRequest) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[12] - 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 RemoveRuleRequest.ProtoReflect.Descriptor instead. -func (*RemoveRuleRequest) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{12} -} - -func (x *RemoveRuleRequest) GetRuleTag() string { - if x != nil { - return x.RuleTag - } - return "" -} - -type RemoveRuleResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RemoveRuleResponse) Reset() { - *x = RemoveRuleResponse{} - mi := &file_app_router_command_command_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveRuleResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveRuleResponse) ProtoMessage() {} - -func (x *RemoveRuleResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[13] - 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 RemoveRuleResponse.ProtoReflect.Descriptor instead. -func (*RemoveRuleResponse) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{13} -} - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -789,9 +302,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_router_command_command_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_command_command_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -801,8 +316,8 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_app_router_command_command_proto_msgTypes[14] - if x != nil { + mi := &file_app_router_command_command_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -814,7 +329,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_app_router_command_command_proto_rawDescGZIP(), []int{14} + return file_app_router_command_command_proto_rawDescGZIP(), []int{3} } var File_app_router_command_command_proto protoreflect.FileDescriptor @@ -825,155 +340,79 @@ var file_app_router_command_command_proto_rawDesc = []byte{ 0x74, 0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75, - 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, - 0x1c, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, - 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, - 0x52, 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, - 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55, - 0x73, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, - 0x57, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, - 0x03, 0x28, 0x0b, 0x32, 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, 0x52, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x75, 0x74, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x46, 0x0a, 0x1c, 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, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, - 0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x52, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, - 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x22, 0x27, 0x0a, 0x13, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, - 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x26, 0x0a, 0x0c, - 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 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, 0xa9, 0x01, 0x0a, 0x0b, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x72, 0x4d, 0x73, 0x67, 0x12, 0x41, 0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 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, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f, - 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63, - 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2c, 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, 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, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x49, 0x6e, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1c, 0x0a, 0x09, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x57, 0x0a, 0x0a, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, + 0x32, 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, 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, + 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, + 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, + 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x54, 0x61, 0x67, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x46, 0x0a, 0x1c, 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, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xb1, 0x01, 0x0a, + 0x10, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4f, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x52, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xf0, 0x01, 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, 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, + 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, 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 ( @@ -988,52 +427,28 @@ func file_app_router_command_command_proto_rawDescGZIP() []byte { return file_app_router_command_command_proto_rawDescData } -var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 16) -var file_app_router_command_command_proto_goTypes = []any{ - (*RoutingContext)(nil), // 0: xray.app.router.command.RoutingContext - (*SubscribeRoutingStatsRequest)(nil), // 1: xray.app.router.command.SubscribeRoutingStatsRequest - (*TestRouteRequest)(nil), // 2: xray.app.router.command.TestRouteRequest - (*PrincipleTargetInfo)(nil), // 3: xray.app.router.command.PrincipleTargetInfo - (*OverrideInfo)(nil), // 4: xray.app.router.command.OverrideInfo - (*BalancerMsg)(nil), // 5: xray.app.router.command.BalancerMsg - (*GetBalancerInfoRequest)(nil), // 6: xray.app.router.command.GetBalancerInfoRequest - (*GetBalancerInfoResponse)(nil), // 7: xray.app.router.command.GetBalancerInfoResponse - (*OverrideBalancerTargetRequest)(nil), // 8: xray.app.router.command.OverrideBalancerTargetRequest - (*OverrideBalancerTargetResponse)(nil), // 9: xray.app.router.command.OverrideBalancerTargetResponse - (*AddRuleRequest)(nil), // 10: xray.app.router.command.AddRuleRequest - (*AddRuleResponse)(nil), // 11: xray.app.router.command.AddRuleResponse - (*RemoveRuleRequest)(nil), // 12: xray.app.router.command.RemoveRuleRequest - (*RemoveRuleResponse)(nil), // 13: xray.app.router.command.RemoveRuleResponse - (*Config)(nil), // 14: xray.app.router.command.Config - nil, // 15: xray.app.router.command.RoutingContext.AttributesEntry - (net.Network)(0), // 16: xray.common.net.Network - (*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage +var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_app_router_command_command_proto_goTypes = []interface{}{ + (*RoutingContext)(nil), // 0: xray.app.router.command.RoutingContext + (*SubscribeRoutingStatsRequest)(nil), // 1: xray.app.router.command.SubscribeRoutingStatsRequest + (*TestRouteRequest)(nil), // 2: xray.app.router.command.TestRouteRequest + (*Config)(nil), // 3: xray.app.router.command.Config + nil, // 4: xray.app.router.command.RoutingContext.AttributesEntry + (net.Network)(0), // 5: xray.common.net.Network } var file_app_router_command_command_proto_depIdxs = []int32{ - 16, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network - 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 - 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 - 5, // 5: xray.app.router.command.GetBalancerInfoResponse.balancer:type_name -> xray.app.router.command.BalancerMsg - 17, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage - 1, // 7: 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 - 6, // 9: 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 - 10, // 11: 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 - 0, // 13: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext - 0, // 14: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext - 7, // 15: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse - 9, // 16: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse - 11, // 17: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse - 13, // 18: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse - 13, // [13:19] is the sub-list for method output_type - 7, // [7:13] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 5, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network + 4, // 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 + 1, // 3: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest + 2, // 4: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest + 0, // 5: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext + 0, // 6: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext + 5, // [5:7] is the sub-list for method output_type + 3, // [3:5] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_app_router_command_command_proto_init() } @@ -1041,13 +456,63 @@ func file_app_router_command_command_proto_init() { if File_app_router_command_command_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_router_command_command_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoutingContext); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_command_command_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeRoutingStatsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_command_command_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TestRouteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_command_command_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_router_command_command_proto_rawDesc, NumEnums: 0, - NumMessages: 16, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/app/router/command/command.proto b/app/router/command/command.proto index 756f8226..5f777044 100644 --- a/app/router/command/command.proto +++ b/app/router/command/command.proto @@ -7,7 +7,6 @@ option java_package = "com.xray.app.router.command"; option java_multiple_files = true; import "common/net/network.proto"; -import "common/serial/typed_message.proto"; // RoutingContext is the context with information relative to routing process. // It conforms to the structure of xray.features.routing.Context and @@ -61,56 +60,10 @@ message TestRouteRequest { bool PublishResult = 3; } -message PrincipleTargetInfo { - repeated string tag = 1; -} - -message OverrideInfo { - string target = 2; -} - -message BalancerMsg { - OverrideInfo override = 5; - PrincipleTargetInfo principle_target = 6; -} - -message GetBalancerInfoRequest { - string tag = 1; -} - -message GetBalancerInfoResponse { - BalancerMsg balancer = 1; -} - -message OverrideBalancerTargetRequest { - string balancerTag = 1; - string target = 2; -} - -message OverrideBalancerTargetResponse {} - -message AddRuleRequest { - xray.common.serial.TypedMessage config = 1; - bool shouldAppend = 2; -} -message AddRuleResponse {} - -message RemoveRuleRequest { - string ruleTag = 1; -} - -message RemoveRuleResponse {} - service RoutingService { rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest) returns (stream RoutingContext) {} rpc TestRoute(TestRouteRequest) returns (RoutingContext) {} - - rpc GetBalancerInfo(GetBalancerInfoRequest) returns (GetBalancerInfoResponse){} - rpc OverrideBalancerTarget(OverrideBalancerTargetRequest) returns (OverrideBalancerTargetResponse) {} - - rpc AddRule(AddRuleRequest) returns (AddRuleResponse) {} - rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse) {} } message Config {} diff --git a/app/router/command/command_grpc.pb.go b/app/router/command/command_grpc.pb.go index 23e03c8a..50c6de29 100644 --- a/app/router/command/command_grpc.pb.go +++ b/app/router/command/command_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.2 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.12 // source: app/router/command/command.proto package command @@ -15,28 +15,15 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - RoutingService_SubscribeRoutingStats_FullMethodName = "/xray.app.router.command.RoutingService/SubscribeRoutingStats" - RoutingService_TestRoute_FullMethodName = "/xray.app.router.command.RoutingService/TestRoute" - RoutingService_GetBalancerInfo_FullMethodName = "/xray.app.router.command.RoutingService/GetBalancerInfo" - RoutingService_OverrideBalancerTarget_FullMethodName = "/xray.app.router.command.RoutingService/OverrideBalancerTarget" - RoutingService_AddRule_FullMethodName = "/xray.app.router.command.RoutingService/AddRule" - RoutingService_RemoveRule_FullMethodName = "/xray.app.router.command.RoutingService/RemoveRule" -) +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 // RoutingServiceClient is the client API for RoutingService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type RoutingServiceClient interface { - SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[RoutingContext], error) + SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (RoutingService_SubscribeRoutingStatsClient, error) TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error) - GetBalancerInfo(ctx context.Context, in *GetBalancerInfoRequest, opts ...grpc.CallOption) (*GetBalancerInfoResponse, error) - OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error) - AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error) - RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error) } type routingServiceClient struct { @@ -47,13 +34,12 @@ func NewRoutingServiceClient(cc grpc.ClientConnInterface) RoutingServiceClient { return &routingServiceClient{cc} } -func (c *routingServiceClient) SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[RoutingContext], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &RoutingService_ServiceDesc.Streams[0], RoutingService_SubscribeRoutingStats_FullMethodName, cOpts...) +func (c *routingServiceClient) SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (RoutingService_SubscribeRoutingStatsClient, error) { + stream, err := c.cc.NewStream(ctx, &RoutingService_ServiceDesc.Streams[0], "/xray.app.router.command.RoutingService/SubscribeRoutingStats", opts...) if err != nil { return nil, err } - x := &grpc.GenericClientStream[SubscribeRoutingStatsRequest, RoutingContext]{ClientStream: stream} + x := &routingServiceSubscribeRoutingStatsClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -63,53 +49,26 @@ func (c *routingServiceClient) SubscribeRoutingStats(ctx context.Context, in *Su return x, nil } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type RoutingService_SubscribeRoutingStatsClient = grpc.ServerStreamingClient[RoutingContext] +type RoutingService_SubscribeRoutingStatsClient interface { + Recv() (*RoutingContext, error) + grpc.ClientStream +} + +type routingServiceSubscribeRoutingStatsClient struct { + grpc.ClientStream +} + +func (x *routingServiceSubscribeRoutingStatsClient) Recv() (*RoutingContext, error) { + m := new(RoutingContext) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} func (c *routingServiceClient) TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RoutingContext) - err := c.cc.Invoke(ctx, RoutingService_TestRoute_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *routingServiceClient) GetBalancerInfo(ctx context.Context, in *GetBalancerInfoRequest, opts ...grpc.CallOption) (*GetBalancerInfoResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetBalancerInfoResponse) - err := c.cc.Invoke(ctx, RoutingService_GetBalancerInfo_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *routingServiceClient) OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(OverrideBalancerTargetResponse) - err := c.cc.Invoke(ctx, RoutingService_OverrideBalancerTarget_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *routingServiceClient) AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(AddRuleResponse) - err := c.cc.Invoke(ctx, RoutingService_AddRule_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *routingServiceClient) RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RemoveRuleResponse) - err := c.cc.Invoke(ctx, RoutingService_RemoveRule_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.router.command.RoutingService/TestRoute", in, out, opts...) if err != nil { return nil, err } @@ -118,44 +77,24 @@ func (c *routingServiceClient) RemoveRule(ctx context.Context, in *RemoveRuleReq // RoutingServiceServer is the server API for RoutingService service. // All implementations must embed UnimplementedRoutingServiceServer -// for forward compatibility. +// for forward compatibility type RoutingServiceServer interface { - SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error + SubscribeRoutingStats(*SubscribeRoutingStatsRequest, RoutingService_SubscribeRoutingStatsServer) error TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) - GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) - OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) - AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) - RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) mustEmbedUnimplementedRoutingServiceServer() } -// UnimplementedRoutingServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedRoutingServiceServer struct{} +// UnimplementedRoutingServiceServer must be embedded to have forward compatible implementations. +type UnimplementedRoutingServiceServer struct { +} -func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error { +func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, RoutingService_SubscribeRoutingStatsServer) error { return status.Errorf(codes.Unimplemented, "method SubscribeRoutingStats not implemented") } func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) { return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented") } -func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetBalancerInfo not implemented") -} -func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method OverrideBalancerTarget not implemented") -} -func (UnimplementedRoutingServiceServer) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AddRule not implemented") -} -func (UnimplementedRoutingServiceServer) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RemoveRule not implemented") -} func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {} -func (UnimplementedRoutingServiceServer) testEmbeddedByValue() {} // UnsafeRoutingServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to RoutingServiceServer will @@ -165,13 +104,6 @@ type UnsafeRoutingServiceServer interface { } func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) { - // If the following call pancis, it indicates UnimplementedRoutingServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&RoutingService_ServiceDesc, srv) } @@ -180,11 +112,21 @@ func _RoutingService_SubscribeRoutingStats_Handler(srv interface{}, stream grpc. if err := stream.RecvMsg(m); err != nil { return err } - return srv.(RoutingServiceServer).SubscribeRoutingStats(m, &grpc.GenericServerStream[SubscribeRoutingStatsRequest, RoutingContext]{ServerStream: stream}) + return srv.(RoutingServiceServer).SubscribeRoutingStats(m, &routingServiceSubscribeRoutingStatsServer{stream}) } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type RoutingService_SubscribeRoutingStatsServer = grpc.ServerStreamingServer[RoutingContext] +type RoutingService_SubscribeRoutingStatsServer interface { + Send(*RoutingContext) error + grpc.ServerStream +} + +type routingServiceSubscribeRoutingStatsServer struct { + grpc.ServerStream +} + +func (x *routingServiceSubscribeRoutingStatsServer) Send(m *RoutingContext) error { + return x.ServerStream.SendMsg(m) +} func _RoutingService_TestRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(TestRouteRequest) @@ -196,7 +138,7 @@ func _RoutingService_TestRoute_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: RoutingService_TestRoute_FullMethodName, + FullMethod: "/xray.app.router.command.RoutingService/TestRoute", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RoutingServiceServer).TestRoute(ctx, req.(*TestRouteRequest)) @@ -204,78 +146,6 @@ func _RoutingService_TestRoute_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _RoutingService_GetBalancerInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetBalancerInfoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutingServiceServer).GetBalancerInfo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: RoutingService_GetBalancerInfo_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutingServiceServer).GetBalancerInfo(ctx, req.(*GetBalancerInfoRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _RoutingService_OverrideBalancerTarget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(OverrideBalancerTargetRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutingServiceServer).OverrideBalancerTarget(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: RoutingService_OverrideBalancerTarget_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutingServiceServer).OverrideBalancerTarget(ctx, req.(*OverrideBalancerTargetRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _RoutingService_AddRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddRuleRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutingServiceServer).AddRule(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: RoutingService_AddRule_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutingServiceServer).AddRule(ctx, req.(*AddRuleRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _RoutingService_RemoveRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveRuleRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutingServiceServer).RemoveRule(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: RoutingService_RemoveRule_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutingServiceServer).RemoveRule(ctx, req.(*RemoveRuleRequest)) - } - return interceptor(ctx, in, info, handler) -} - // RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -287,22 +157,6 @@ var RoutingService_ServiceDesc = grpc.ServiceDesc{ MethodName: "TestRoute", Handler: _RoutingService_TestRoute_Handler, }, - { - MethodName: "GetBalancerInfo", - Handler: _RoutingService_GetBalancerInfo_Handler, - }, - { - MethodName: "OverrideBalancerTarget", - Handler: _RoutingService_OverrideBalancerTarget_Handler, - }, - { - MethodName: "AddRule", - Handler: _RoutingService_AddRule_Handler, - }, - { - MethodName: "RemoveRule", - Handler: _RoutingService_RemoveRule_Handler, - }, }, Streams: []grpc.StreamDesc{ { diff --git a/app/router/command/command_test.go b/app/router/command/command_test.go index e8329695..b9d252a6 100644 --- a/app/router/command/command_test.go +++ b/app/router/command/command_test.go @@ -16,7 +16,6 @@ import ( "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/testing/mocks" "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/test/bufconn" ) @@ -81,7 +80,7 @@ func TestServiceSubscribeRoutingStats(t *testing.T) { // Client goroutine go func() { defer lis.Close() - conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) if err != nil { errCh <- err return @@ -215,7 +214,7 @@ func TestServiceSubscribeSubsetOfFields(t *testing.T) { // Client goroutine go func() { defer lis.Close() - conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) if err != nil { errCh <- err return @@ -272,7 +271,7 @@ func TestServiceSubscribeSubsetOfFields(t *testing.T) { } } -func TestServiceTestRoute(t *testing.T) { +func TestSerivceTestRoute(t *testing.T) { c := stats.NewChannel(&stats.ChannelConfig{ SubscriberLimit: 1, BufferSize: 16, @@ -319,7 +318,7 @@ func TestServiceTestRoute(t *testing.T) { TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, }, - }, mocks.NewDNSClient(mockCtl), mocks.NewOutboundManager(mockCtl), nil)) + }, mocks.NewDNSClient(mockCtl), mocks.NewOutboundManager(mockCtl))) lis := bufconn.Listen(1024 * 1024) bufDialer := func(context.Context, string) (net.Conn, error) { @@ -338,7 +337,7 @@ func TestServiceTestRoute(t *testing.T) { // Client goroutine go func() { defer lis.Close() - conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) if err != nil { errCh <- err } diff --git a/app/router/command/config.go b/app/router/command/config.go index 8c2e1343..a033e731 100644 --- a/app/router/command/config.go +++ b/app/router/command/config.go @@ -28,10 +28,6 @@ func (c routingContext) GetTargetPort() net.Port { return net.Port(c.RoutingContext.GetTargetPort()) } -func (c routingContext) GetRuleTag() string { - return "" -} - // GetSkipDNSResolve is a mock implementation here to match the interface, // SkipDNSResolve is set from dns module, no use if coming from a protobuf object? // TODO: please confirm @Vigilans diff --git a/app/router/command/errors.generated.go b/app/router/command/errors.generated.go new file mode 100644 index 00000000..a1305932 --- /dev/null +++ b/app/router/command/errors.generated.go @@ -0,0 +1,9 @@ +package command + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/router/condition.go b/app/router/condition.go index 35e2424d..cdcb6747 100644 --- a/app/router/condition.go +++ b/app/router/condition.go @@ -1,13 +1,13 @@ package router import ( - "regexp" "strings" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/strmatcher" "github.com/xtls/xray-core/features/routing" + "go.starlark.net/starlark" + "go.starlark.net/syntax" ) type Condition interface { @@ -50,12 +50,12 @@ var matcherTypeMap = map[Domain_Type]strmatcher.Type{ func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) { matcherType, f := matcherTypeMap[domain.Type] if !f { - return nil, errors.New("unsupported domain type", domain.Type) + return nil, newError("unsupported domain type", domain.Type) } matcher, err := matcherType.New(domain.Value) if err != nil { - return nil, errors.New("failed to create domain matcher").Base(err) + return nil, newError("failed to create domain matcher").Base(err) } return matcher, nil @@ -70,7 +70,7 @@ func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) { for _, d := range domains { matcherType, f := matcherTypeMap[d.Type] if !f { - return nil, errors.New("unsupported domain type", d.Type) + return nil, newError("unsupported domain type", d.Type) } _, err := g.AddPattern(d.Value, matcherType) if err != nil { @@ -119,7 +119,7 @@ type MultiGeoIPMatcher struct { func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, error) { var matchers []*GeoIPMatcher for _, geoip := range geoips { - matcher, err := GlobalGeoIPContainer.Add(geoip) + matcher, err := globalGeoIPContainer.Add(geoip) if err != nil { return nil, err } @@ -192,28 +192,18 @@ func (v NetworkMatcher) Apply(ctx routing.Context) bool { } type UserMatcher struct { - user []string - pattern []*regexp.Regexp + user []string } func NewUserMatcher(users []string) *UserMatcher { usersCopy := make([]string, 0, len(users)) - patternsCopy := make([]*regexp.Regexp, 0, len(users)) for _, user := range users { if len(user) > 0 { - if len(user) > 7 && strings.HasPrefix(user, "regexp:") { - if re, err := regexp.Compile(user[7:]); err == nil { - patternsCopy = append(patternsCopy, re) - } - // Items of users slice with an invalid regexp syntax are ignored. - continue - } usersCopy = append(usersCopy, user) } } return &UserMatcher{ - user: usersCopy, - pattern: patternsCopy, + user: usersCopy, } } @@ -228,11 +218,6 @@ func (v *UserMatcher) Apply(ctx routing.Context) bool { return true } } - for _, re := range v.pattern { - if re.MatchString(user) { - return true - } - } return false } @@ -299,22 +284,44 @@ func (m *ProtocolMatcher) Apply(ctx routing.Context) bool { } type AttributeMatcher struct { - configuredKeys map[string]*regexp.Regexp + program *starlark.Program +} + +func NewAttributeMatcher(code string) (*AttributeMatcher, error) { + starFile, err := syntax.Parse("attr.star", "satisfied=("+code+")", 0) + if err != nil { + return nil, newError("attr rule").Base(err) + } + p, err := starlark.FileProgram(starFile, func(name string) bool { + return name == "attrs" + }) + if err != nil { + return nil, err + } + return &AttributeMatcher{ + program: p, + }, nil } // Match implements attributes matching. func (m *AttributeMatcher) Match(attrs map[string]string) bool { - // header keys are case insensitive most likely. So we do a convert - httpHeaders := make(map[string]string) + attrsDict := new(starlark.Dict) for key, value := range attrs { - httpHeaders[strings.ToLower(key)] = value + attrsDict.SetKey(starlark.String(key), starlark.String(value)) } - for key, regex := range m.configuredKeys { - if a, ok := httpHeaders[key]; !ok || !regex.MatchString(a) { - return false - } + + predefined := make(starlark.StringDict) + predefined["attrs"] = attrsDict + + thread := &starlark.Thread{ + Name: "matcher", } - return true + results, err := m.program.Init(thread, predefined) + if err != nil { + newError("attr matcher").Base(err).WriteToLog() + } + satisfied := results["satisfied"] + return satisfied != nil && bool(satisfied.Truth()) } // Apply implements Condition. diff --git a/app/router/condition_geoip.go b/app/router/condition_geoip.go index 38f7f0ce..eb47be83 100644 --- a/app/router/condition_geoip.go +++ b/app/router/condition_geoip.go @@ -1,49 +1,81 @@ package router import ( - "net/netip" - "strconv" + "encoding/binary" + "sort" "github.com/xtls/xray-core/common/net" - "go4.org/netipx" ) +type ipv6 struct { + a uint64 + b uint64 +} + type GeoIPMatcher struct { countryCode string reverseMatch bool - ip4 *netipx.IPSet - ip6 *netipx.IPSet + ip4 []uint32 + prefix4 []uint8 + ip6 []ipv6 + prefix6 []uint8 +} + +func normalize4(ip uint32, prefix uint8) uint32 { + return (ip >> (32 - prefix)) << (32 - prefix) +} + +func normalize6(ip ipv6, prefix uint8) ipv6 { + if prefix <= 64 { + ip.a = (ip.a >> (64 - prefix)) << (64 - prefix) + ip.b = 0 + } else { + ip.b = (ip.b >> (128 - prefix)) << (128 - prefix) + } + return ip } func (m *GeoIPMatcher) Init(cidrs []*CIDR) error { - var builder4, builder6 netipx.IPSetBuilder + ip4Count := 0 + ip6Count := 0 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 - } - + ip := cidr.Ip switch len(ip) { - case net.IPv4len: - builder4.AddPrefix(ipPrefix) - case net.IPv6len: - builder6.AddPrefix(ipPrefix) + case 4: + ip4Count++ + case 16: + ip6Count++ + default: + return newError("unexpect ip length: ", len(ip)) } } - if ip4, err := builder4.IPSet(); err != nil { - return err - } else { - m.ip4 = ip4 - } + cidrList := CIDRList(cidrs) + sort.Sort(&cidrList) - if ip6, err := builder6.IPSet(); err != nil { - return err - } else { - m.ip6 = ip6 + m.ip4 = make([]uint32, 0, ip4Count) + m.prefix4 = make([]uint8, 0, ip4Count) + m.ip6 = make([]ipv6, 0, ip6Count) + m.prefix6 = make([]uint8, 0, ip6Count) + + for _, cidr := range cidrList { + ip := cidr.Ip + prefix := uint8(cidr.Prefix) + switch len(ip) { + case 4: + m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix)) + m.prefix4 = append(m.prefix4, prefix) + case 16: + ip6 := ipv6{ + a: binary.BigEndian.Uint64(ip[0:8]), + b: binary.BigEndian.Uint64(ip[8:16]), + } + ip6 = normalize6(ip6, prefix) + + m.ip6 = append(m.ip6, ip6) + m.prefix6 = append(m.prefix6, prefix) + } } return nil @@ -53,37 +85,91 @@ func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) { m.reverseMatch = isReverseMatch } -func (m *GeoIPMatcher) match4(ip net.IP) bool { - nip, ok := netipx.FromStdIP(ip) - if !ok { +func (m *GeoIPMatcher) match4(ip uint32) bool { + if len(m.ip4) == 0 { return false } - return m.ip4.Contains(nip) + if ip < m.ip4[0] { + return false + } + + size := uint32(len(m.ip4)) + l := uint32(0) + r := size + for l < r { + x := ((l + r) >> 1) + if ip < m.ip4[x] { + r = x + continue + } + + nip := normalize4(ip, m.prefix4[x]) + if nip == m.ip4[x] { + return true + } + + l = x + 1 + } + + return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1] } -func (m *GeoIPMatcher) match6(ip net.IP) bool { - nip, ok := netipx.FromStdIP(ip) - if !ok { +func less6(a ipv6, b ipv6) bool { + return a.a < b.a || (a.a == b.a && a.b < b.b) +} + +func (m *GeoIPMatcher) match6(ip ipv6) bool { + if len(m.ip6) == 0 { return false } - return m.ip6.Contains(nip) + if less6(ip, m.ip6[0]) { + return false + } + + size := uint32(len(m.ip6)) + l := uint32(0) + r := size + for l < r { + x := (l + r) / 2 + if less6(ip, m.ip6[x]) { + r = x + continue + } + + if normalize6(ip, m.prefix6[x]) == m.ip6[x] { + return true + } + + l = x + 1 + } + + return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1] } // 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) + case 4: + if m.reverseMatch { + return !m.match4(binary.BigEndian.Uint32(ip)) + } + return m.match4(binary.BigEndian.Uint32(ip)) + case 16: + if m.reverseMatch { + return !m.match6(ipv6{ + a: binary.BigEndian.Uint64(ip[0:8]), + b: binary.BigEndian.Uint64(ip[8:16]), + }) + } + return m.match6(ipv6{ + a: binary.BigEndian.Uint64(ip[0:8]), + b: binary.BigEndian.Uint64(ip[8:16]), + }) + default: + return false } - if m.reverseMatch { - return !isMatched - } - return isMatched } // GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code. @@ -115,30 +201,4 @@ func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) { 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 -} +var globalGeoIPContainer GeoIPMatcherContainer diff --git a/app/router/condition_geoip_test.go b/app/router/condition_geoip_test.go index 07f40b83..b5a5ef90 100644 --- a/app/router/condition_geoip_test.go +++ b/app/router/condition_geoip_test.go @@ -1,38 +1,28 @@ package router_test import ( - "fmt" "os" "path/filepath" "testing" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" "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) - } +func init() { + wd, err := os.Getwd() + common.Must(err) - return path, nil + if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) { + common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "resources", "geoip.dat"))) + } + if _, err := os.Stat(platform.GetAssetLocation("geosite.dat")); err != nil && os.IsNotExist(err) { + common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "resources", "geosite.dat"))) + } } func TestGeoIPMatcherContainer(t *testing.T) { @@ -63,7 +53,7 @@ func TestGeoIPMatcherContainer(t *testing.T) { } func TestGeoIPMatcher(t *testing.T) { - cidrList := []*router.CIDR{ + cidrList := router.CIDRList{ {Ip: []byte{0, 0, 0, 0}, Prefix: 8}, {Ip: []byte{10, 0, 0, 0}, Prefix: 8}, {Ip: []byte{100, 64, 0, 0}, Prefix: 10}, @@ -134,40 +124,8 @@ func TestGeoIPMatcher(t *testing.T) { } } -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{ + cidrList := router.CIDRList{ {Ip: []byte{8, 8, 8, 8}, Prefix: 32}, {Ip: []byte{91, 108, 4, 0}, Prefix: 16}, } @@ -227,15 +185,10 @@ func TestGeoIPMatcher6US(t *testing.T) { } func loadGeoIP(country string) ([]*router.CIDR, error) { - path, err := getAssetPath("geoip.dat") + geoipBytes, err := filesystem.ReadAsset("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 diff --git a/app/router/condition_test.go b/app/router/condition_test.go index 97d05db9..5d98eb43 100644 --- a/app/router/condition_test.go +++ b/app/router/condition_test.go @@ -1,22 +1,37 @@ package router_test import ( + "os" + "path/filepath" "strconv" "testing" + "github.com/golang/protobuf/proto" . "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol/http" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/features/routing" routing_session "github.com/xtls/xray-core/features/routing/session" - "google.golang.org/protobuf/proto" ) +func init() { + wd, err := os.Getwd() + common.Must(err) + + if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) { + common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "release", "config", "geoip.dat"))) + } + if _, err := os.Stat(platform.GetAssetLocation("geosite.dat")); err != nil && os.IsNotExist(err) { + common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "release", "config", "geosite.dat"))) + } +} + func withBackground() routing.Context { return &routing_session.Context{} } @@ -91,6 +106,42 @@ func TestRoutingRule(t *testing.T) { }, }, }, + { + rule: &RoutingRule{ + Cidr: []*CIDR{ + { + Ip: []byte{8, 8, 8, 8}, + Prefix: 32, + }, + { + Ip: []byte{8, 8, 8, 8}, + Prefix: 32, + }, + { + Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(), + Prefix: 128, + }, + }, + }, + test: []ruleTest{ + { + input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}), + output: true, + }, + { + input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.4.4"), 80)}), + output: false, + }, + { + input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 80)}), + output: true, + }, + { + input: withBackground(), + output: false, + }, + }, + }, { rule: &RoutingRule{ Geoip: []*GeoIP{ @@ -133,14 +184,10 @@ func TestRoutingRule(t *testing.T) { }, { rule: &RoutingRule{ - SourceGeoip: []*GeoIP{ + SourceCidr: []*CIDR{ { - Cidr: []*CIDR{ - { - Ip: []byte{192, 168, 0, 0}, - Prefix: 16, - }, - }, + Ip: []byte{192, 168, 0, 0}, + Prefix: 16, }, }, }, @@ -260,10 +307,8 @@ func TestRoutingRule(t *testing.T) { }, { rule: &RoutingRule{ - Protocol: []string{"http"}, - Attributes: map[string]string{ - ":path": "/test", - }, + Protocol: []string{"http"}, + Attributes: "attrs[':path'].startswith('/test')", }, test: []ruleTest{ { @@ -272,19 +317,6 @@ func TestRoutingRule(t *testing.T) { }, }, }, - { - rule: &RoutingRule{ - Attributes: map[string]string{ - "Custom": "p([a-z]+)ch", - }, - }, - test: []ruleTest{ - { - input: withContent(&session.Content{Attributes: map[string]string{"custom": "peach"}}), - output: true, - }, - }, - }, } for _, test := range cases { @@ -301,15 +333,10 @@ func TestRoutingRule(t *testing.T) { } func loadGeoSite(country string) ([]*Domain, error) { - path, err := getAssetPath("geosite.dat") + geositeBytes, err := filesystem.ReadAsset("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 diff --git a/app/router/config.go b/app/router/config.go index f1740610..9bb4bc46 100644 --- a/app/router/config.go +++ b/app/router/config.go @@ -1,18 +1,52 @@ package router import ( - "context" - "regexp" - "strings" - - "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/routing" ) +// CIDRList is an alias of []*CIDR to provide sort.Interface. +type CIDRList []*CIDR + +// Len implements sort.Interface. +func (l *CIDRList) Len() int { + return len(*l) +} + +// Less implements sort.Interface. +func (l *CIDRList) Less(i int, j int) bool { + ci := (*l)[i] + cj := (*l)[j] + + if len(ci.Ip) < len(cj.Ip) { + return true + } + + if len(ci.Ip) > len(cj.Ip) { + return false + } + + for k := 0; k < len(ci.Ip); k++ { + if ci.Ip[k] < cj.Ip[k] { + return true + } + + if ci.Ip[k] > cj.Ip[k] { + return false + } + } + + return ci.Prefix < cj.Prefix +} + +// Swap implements sort.Interface. +func (l *CIDRList) Swap(i int, j int) { + (*l)[i], (*l)[j] = (*l)[j], (*l)[i] +} + type Rule struct { Tag string - RuleTag string Balancer *Balancer Condition Condition } @@ -37,7 +71,7 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { case "linear": matcher, err := NewDomainMatcher(rr.Domain) if err != nil { - return nil, errors.New("failed to build domain condition").Base(err) + return nil, newError("failed to build domain condition").Base(err) } conds.Add(matcher) case "mph", "hybrid": @@ -45,9 +79,9 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { default: matcher, err := NewMphMatcherGroup(rr.Domain) if err != nil { - return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err) + return nil, newError("failed to build domain condition with MphDomainMatcher").Base(err) } - errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)") + newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog() conds.Add(matcher) } } @@ -62,6 +96,8 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { if rr.PortList != nil { conds.Add(NewPortMatcher(rr.PortList, false)) + } else if rr.PortRange != nil { + conds.Add(NewPortMatcher(&net.PortList{Range: []*net.PortRange{rr.PortRange}}, false)) } if rr.SourcePortList != nil { @@ -70,6 +106,8 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { if len(rr.Networks) > 0 { conds.Add(NewNetworkMatcher(rr.Networks)) + } else if rr.NetworkList != nil { + conds.Add(NewNetworkMatcher(rr.NetworkList.Network)) } if len(rr.Geoip) > 0 { @@ -78,6 +116,12 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { return nil, err } conds.Add(cond) + } else if len(rr.Cidr) > 0 { + cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.Cidr}}, false) + if err != nil { + return nil, err + } + conds.Add(cond) } if len(rr.SourceGeoip) > 0 { @@ -86,6 +130,12 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { return nil, err } conds.Add(cond) + } else if len(rr.SourceCidr) > 0 { + cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.SourceCidr}}, true) + if err != nil { + return nil, err + } + conds.Add(cond) } if len(rr.Protocol) > 0 { @@ -93,63 +143,36 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { } if len(rr.Attributes) > 0 { - configuredKeys := make(map[string]*regexp.Regexp) - for key, value := range rr.Attributes { - configuredKeys[strings.ToLower(key)] = regexp.MustCompile(value) + cond, err := NewAttributeMatcher(rr.Attributes) + if err != nil { + return nil, err } - conds.Add(&AttributeMatcher{configuredKeys}) + conds.Add(cond) } if conds.Len() == 0 { - return nil, errors.New("this rule has no effective fields").AtWarning() + return nil, newError("this rule has no effective fields").AtWarning() } return conds, nil } -// Build builds the balancing rule -func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatcher) (*Balancer, error) { - switch strings.ToLower(br.Strategy) { - case "leastping": +func (br *BalancingRule) Build(ohm outbound.Manager) (*Balancer, error) { + switch br.Strategy { + case "leastPing": return &Balancer{ - selectors: br.OutboundSelector, - strategy: &LeastPingStrategy{}, - fallbackTag: br.FallbackTag, - ohm: ohm, - }, nil - case "roundrobin": - return &Balancer{ - selectors: br.OutboundSelector, - strategy: &RoundRobinStrategy{FallbackTag: br.FallbackTag}, - fallbackTag: br.FallbackTag, - ohm: ohm, - }, nil - case "leastload": - i, err := br.StrategySettings.GetInstance() - if err != nil { - return nil, err - } - s, ok := i.(*StrategyLeastLoadConfig) - if !ok { - return nil, errors.New("not a StrategyLeastLoadConfig").AtError() - } - leastLoadStrategy := NewLeastLoadStrategy(s) - return &Balancer{ - selectors: br.OutboundSelector, - ohm: ohm, - fallbackTag: br.FallbackTag, - strategy: leastLoadStrategy, + selectors: br.OutboundSelector, + strategy: &LeastPingStrategy{}, + ohm: ohm, }, nil case "random": fallthrough - case "": - return &Balancer{ - selectors: br.OutboundSelector, - ohm: ohm, - fallbackTag: br.FallbackTag, - strategy: &RandomStrategy{FallbackTag: br.FallbackTag}, - }, nil default: - return nil, errors.New("unrecognized balancer type") + return &Balancer{ + selectors: br.OutboundSelector, + strategy: &RandomStrategy{}, + ohm: ohm, + }, nil + } } diff --git a/app/router/config.pb.go b/app/router/config.pb.go index ca6cc38f..0089a1a8 100644 --- a/app/router/config.pb.go +++ b/app/router/config.pb.go @@ -1,14 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/router/config.proto package router import ( net "github.com/xtls/xray-core/common/net" - serial "github.com/xtls/xray-core/common/serial" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -132,7 +131,7 @@ func (x Config_DomainStrategy) Number() protoreflect.EnumNumber { // Deprecated: Use Config_DomainStrategy.Descriptor instead. func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{10, 0} + return file_app_router_config_proto_rawDescGZIP(), []int{8, 0} } // Domain for routing decision. @@ -151,9 +150,11 @@ type Domain struct { func (x *Domain) Reset() { *x = Domain{} - mi := &file_app_router_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Domain) String() string { @@ -164,7 +165,7 @@ func (*Domain) ProtoMessage() {} func (x *Domain) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -214,9 +215,11 @@ type CIDR struct { func (x *CIDR) Reset() { *x = CIDR{} - mi := &file_app_router_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *CIDR) String() string { @@ -227,7 +230,7 @@ func (*CIDR) ProtoMessage() {} func (x *CIDR) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -268,9 +271,11 @@ type GeoIP struct { func (x *GeoIP) Reset() { *x = GeoIP{} - mi := &file_app_router_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GeoIP) String() string { @@ -281,7 +286,7 @@ func (*GeoIP) ProtoMessage() {} func (x *GeoIP) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -327,9 +332,11 @@ type GeoIPList struct { func (x *GeoIPList) Reset() { *x = GeoIPList{} - mi := &file_app_router_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GeoIPList) String() string { @@ -340,7 +347,7 @@ func (*GeoIPList) ProtoMessage() {} func (x *GeoIPList) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -373,9 +380,11 @@ type GeoSite struct { func (x *GeoSite) Reset() { *x = GeoSite{} - mi := &file_app_router_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GeoSite) String() string { @@ -386,7 +395,7 @@ func (*GeoSite) ProtoMessage() {} func (x *GeoSite) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -425,9 +434,11 @@ type GeoSiteList struct { func (x *GeoSiteList) Reset() { *x = GeoSiteList{} - mi := &file_app_router_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GeoSiteList) String() string { @@ -438,7 +449,7 @@ func (*GeoSiteList) ProtoMessage() {} func (x *GeoSiteList) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -470,35 +481,54 @@ type RoutingRule struct { // *RoutingRule_Tag // *RoutingRule_BalancingTag TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"` - RuleTag string `protobuf:"bytes,18,opt,name=rule_tag,json=ruleTag,proto3" json:"rule_tag,omitempty"` // List of domains for target domain matching. Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"` + // List of CIDRs for target IP address matching. + // Deprecated. Use geoip below. + // + // Deprecated: Do not use. + Cidr []*CIDR `protobuf:"bytes,3,rep,name=cidr,proto3" json:"cidr,omitempty"` // List of GeoIPs for target IP address matching. If this entry exists, the // 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. Geoip []*GeoIP `protobuf:"bytes,10,rep,name=geoip,proto3" json:"geoip,omitempty"` + // A range of port [from, to]. If the destination port is in this range, this + // rule takes effect. Deprecated. Use port_list. + // + // Deprecated: Do not use. + PortRange *net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"` // List of ports. PortList *net.PortList `protobuf:"bytes,14,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"` + // List of networks. Deprecated. Use networks. + // + // Deprecated: Do not use. + NetworkList *net.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"` // List of networks for matching. Networks []net.Network `protobuf:"varint,13,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"` + // List of CIDRs for source IP address matching. + // + // Deprecated: Do not use. + SourceCidr []*CIDR `protobuf:"bytes,6,rep,name=source_cidr,json=sourceCidr,proto3" json:"source_cidr,omitempty"` // List of GeoIPs for source IP address matching. If this entry exists, the // source_cidr above will have no effect. SourceGeoip []*GeoIP `protobuf:"bytes,11,rep,name=source_geoip,json=sourceGeoip,proto3" json:"source_geoip,omitempty"` // List of ports for source port matching. - SourcePortList *net.PortList `protobuf:"bytes,16,opt,name=source_port_list,json=sourcePortList,proto3" json:"source_port_list,omitempty"` - UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"` - InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"` - Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"` - Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"` + SourcePortList *net.PortList `protobuf:"bytes,16,opt,name=source_port_list,json=sourcePortList,proto3" json:"source_port_list,omitempty"` + UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"` + InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"` + Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"` + Attributes string `protobuf:"bytes,15,opt,name=attributes,proto3" json:"attributes,omitempty"` + DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"` } func (x *RoutingRule) Reset() { *x = RoutingRule{} - mi := &file_app_router_config_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RoutingRule) String() string { @@ -509,7 +539,7 @@ func (*RoutingRule) ProtoMessage() {} func (x *RoutingRule) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -545,13 +575,6 @@ func (x *RoutingRule) GetBalancingTag() string { return "" } -func (x *RoutingRule) GetRuleTag() string { - if x != nil { - return x.RuleTag - } - return "" -} - func (x *RoutingRule) GetDomain() []*Domain { if x != nil { return x.Domain @@ -559,6 +582,14 @@ func (x *RoutingRule) GetDomain() []*Domain { return nil } +// Deprecated: Do not use. +func (x *RoutingRule) GetCidr() []*CIDR { + if x != nil { + return x.Cidr + } + return nil +} + func (x *RoutingRule) GetGeoip() []*GeoIP { if x != nil { return x.Geoip @@ -566,6 +597,14 @@ func (x *RoutingRule) GetGeoip() []*GeoIP { return nil } +// Deprecated: Do not use. +func (x *RoutingRule) GetPortRange() *net.PortRange { + if x != nil { + return x.PortRange + } + return nil +} + func (x *RoutingRule) GetPortList() *net.PortList { if x != nil { return x.PortList @@ -573,6 +612,14 @@ func (x *RoutingRule) GetPortList() *net.PortList { return nil } +// Deprecated: Do not use. +func (x *RoutingRule) GetNetworkList() *net.NetworkList { + if x != nil { + return x.NetworkList + } + return nil +} + func (x *RoutingRule) GetNetworks() []net.Network { if x != nil { return x.Networks @@ -580,6 +627,14 @@ func (x *RoutingRule) GetNetworks() []net.Network { return nil } +// Deprecated: Do not use. +func (x *RoutingRule) GetSourceCidr() []*CIDR { + if x != nil { + return x.SourceCidr + } + return nil +} + func (x *RoutingRule) GetSourceGeoip() []*GeoIP { if x != nil { return x.SourceGeoip @@ -615,11 +670,11 @@ func (x *RoutingRule) GetProtocol() []string { return nil } -func (x *RoutingRule) GetAttributes() map[string]string { +func (x *RoutingRule) GetAttributes() string { if x != nil { return x.Attributes } - return nil + return "" } func (x *RoutingRule) GetDomainMatcher() string { @@ -652,18 +707,18 @@ type BalancingRule struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` - OutboundSelector []string `protobuf:"bytes,2,rep,name=outbound_selector,json=outboundSelector,proto3" json:"outbound_selector,omitempty"` - Strategy string `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"` - StrategySettings *serial.TypedMessage `protobuf:"bytes,4,opt,name=strategy_settings,json=strategySettings,proto3" json:"strategy_settings,omitempty"` - FallbackTag string `protobuf:"bytes,5,opt,name=fallback_tag,json=fallbackTag,proto3" json:"fallback_tag,omitempty"` + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` + OutboundSelector []string `protobuf:"bytes,2,rep,name=outbound_selector,json=outboundSelector,proto3" json:"outbound_selector,omitempty"` + Strategy string `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"` } func (x *BalancingRule) Reset() { *x = BalancingRule{} - mi := &file_app_router_config_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *BalancingRule) String() string { @@ -674,7 +729,7 @@ func (*BalancingRule) ProtoMessage() {} func (x *BalancingRule) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[7] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -710,163 +765,6 @@ func (x *BalancingRule) GetStrategy() string { return "" } -func (x *BalancingRule) GetStrategySettings() *serial.TypedMessage { - if x != nil { - return x.StrategySettings - } - return nil -} - -func (x *BalancingRule) GetFallbackTag() string { - if x != nil { - return x.FallbackTag - } - return "" -} - -type StrategyWeight struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Regexp bool `protobuf:"varint,1,opt,name=regexp,proto3" json:"regexp,omitempty"` - Match string `protobuf:"bytes,2,opt,name=match,proto3" json:"match,omitempty"` - Value float32 `protobuf:"fixed32,3,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *StrategyWeight) Reset() { - *x = StrategyWeight{} - mi := &file_app_router_config_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StrategyWeight) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StrategyWeight) ProtoMessage() {} - -func (x *StrategyWeight) ProtoReflect() protoreflect.Message { - mi := &file_app_router_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 StrategyWeight.ProtoReflect.Descriptor instead. -func (*StrategyWeight) Descriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{8} -} - -func (x *StrategyWeight) GetRegexp() bool { - if x != nil { - return x.Regexp - } - return false -} - -func (x *StrategyWeight) GetMatch() string { - if x != nil { - return x.Match - } - return "" -} - -func (x *StrategyWeight) GetValue() float32 { - if x != nil { - return x.Value - } - return 0 -} - -type StrategyLeastLoadConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // weight settings - Costs []*StrategyWeight `protobuf:"bytes,2,rep,name=costs,proto3" json:"costs,omitempty"` - // RTT baselines for selecting, int64 values of time.Duration - Baselines []int64 `protobuf:"varint,3,rep,packed,name=baselines,proto3" json:"baselines,omitempty"` - // expected nodes count to select - Expected int32 `protobuf:"varint,4,opt,name=expected,proto3" json:"expected,omitempty"` - // max acceptable rtt, filter away high delay nodes. default 0 - MaxRTT int64 `protobuf:"varint,5,opt,name=maxRTT,proto3" json:"maxRTT,omitempty"` - // acceptable failure rate - Tolerance float32 `protobuf:"fixed32,6,opt,name=tolerance,proto3" json:"tolerance,omitempty"` -} - -func (x *StrategyLeastLoadConfig) Reset() { - *x = StrategyLeastLoadConfig{} - mi := &file_app_router_config_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StrategyLeastLoadConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StrategyLeastLoadConfig) ProtoMessage() {} - -func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message { - mi := &file_app_router_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 StrategyLeastLoadConfig.ProtoReflect.Descriptor instead. -func (*StrategyLeastLoadConfig) Descriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{9} -} - -func (x *StrategyLeastLoadConfig) GetCosts() []*StrategyWeight { - if x != nil { - return x.Costs - } - return nil -} - -func (x *StrategyLeastLoadConfig) GetBaselines() []int64 { - if x != nil { - return x.Baselines - } - return nil -} - -func (x *StrategyLeastLoadConfig) GetExpected() int32 { - if x != nil { - return x.Expected - } - return 0 -} - -func (x *StrategyLeastLoadConfig) GetMaxRTT() int64 { - if x != nil { - return x.MaxRTT - } - return 0 -} - -func (x *StrategyLeastLoadConfig) GetTolerance() float32 { - if x != nil { - return x.Tolerance - } - return 0 -} - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -879,9 +777,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_router_config_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -891,8 +791,8 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_app_router_config_proto_msgTypes[10] - if x != nil { + mi := &file_app_router_config_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -904,7 +804,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{10} + return file_app_router_config_proto_rawDescGZIP(), []int{8} } func (x *Config) GetDomainStrategy() Config_DomainStrategy { @@ -943,9 +843,11 @@ type Domain_Attribute struct { func (x *Domain_Attribute) Reset() { *x = Domain_Attribute{} - mi := &file_app_router_config_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_router_config_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Domain_Attribute) String() string { @@ -955,8 +857,8 @@ func (x *Domain_Attribute) String() string { func (*Domain_Attribute) ProtoMessage() {} func (x *Domain_Attribute) ProtoReflect() protoreflect.Message { - mi := &file_app_router_config_proto_msgTypes[11] - if x != nil { + mi := &file_app_router_config_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1020,156 +922,136 @@ var File_app_router_config_proto protoreflect.FileDescriptor var file_app_router_config_proto_rawDesc = []byte{ 0x0a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, - 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, - 0x02, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, - 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x1a, 0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x32, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, - 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0a, - 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, - 0x6c, 0x6c, 0x10, 0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43, 0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x22, 0x7a, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a, - 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, - 0x12, 0x29, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x2e, 0x43, 0x49, 0x44, 0x52, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x72, - 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, - 0x22, 0x39, 0x0a, 0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2c, 0x0a, - 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, - 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x5d, 0x0a, 0x07, 0x47, - 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, - 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x3d, 0x0a, 0x0b, 0x47, 0x65, - 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, - 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, - 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xce, 0x05, 0x0a, 0x0b, 0x52, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, - 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, - 0x67, 0x54, 0x61, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x67, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x12, - 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x36, + 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x1a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x02, 0x0a, 0x06, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, + 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x1a, + 0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1f, + 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0d, + 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x32, 0x0a, + 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x10, 0x00, + 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, + 0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43, 0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, + 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, + 0x78, 0x22, 0x7a, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x29, 0x0a, + 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x78, 0x72, + 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49, + 0x44, 0x52, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x39, 0x0a, + 0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x05, 0x65, 0x6e, + 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, + 0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x5d, 0x0a, 0x07, 0x47, 0x65, 0x6f, 0x53, + 0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, + 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, + 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, + 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x3d, 0x0a, 0x0b, 0x47, 0x65, 0x6f, 0x53, 0x69, + 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, + 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xb5, 0x06, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0d, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x54, 0x61, + 0x67, 0x12, 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x12, 0x2d, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x63, 0x69, 0x64, + 0x72, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12, + 0x3d, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0e, 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, 0x34, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x39, 0x0a, 0x0c, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0b, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x10, 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, 0x0e, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x69, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x4c, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x1a, 0x3d, 0x0a, - 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0c, 0x0a, 0x0a, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, - 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 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, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, - 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61, - 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x67, 0x22, 0x54, 0x0a, 0x0e, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x67, 0x65, 0x78, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x67, - 0x65, 0x78, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0xc0, 0x01, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4c, 0x65, 0x61, 0x73, - 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x05, 0x63, - 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x63, 0x6f, 0x73, - 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, - 0x78, 0x52, 0x54, 0x54, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, - 0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, - 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, - 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, - 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, - 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, - 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, - 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, - 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, - 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, - 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, - 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 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, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, - 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x08, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x69, 0x64, 0x72, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, + 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x69, 0x64, 0x72, 0x12, 0x39, 0x0a, + 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0b, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0b, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x10, 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, 0x0e, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, + 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, + 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, + 0x42, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0x6a, + 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, + 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, + 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, + 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, + 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, + 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, + 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, + 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, + 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 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, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, + 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -1185,51 +1067,50 @@ func file_app_router_config_proto_rawDescGZIP() []byte { } var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_app_router_config_proto_goTypes = []any{ - (Domain_Type)(0), // 0: xray.app.router.Domain.Type - (Config_DomainStrategy)(0), // 1: xray.app.router.Config.DomainStrategy - (*Domain)(nil), // 2: xray.app.router.Domain - (*CIDR)(nil), // 3: xray.app.router.CIDR - (*GeoIP)(nil), // 4: xray.app.router.GeoIP - (*GeoIPList)(nil), // 5: xray.app.router.GeoIPList - (*GeoSite)(nil), // 6: xray.app.router.GeoSite - (*GeoSiteList)(nil), // 7: xray.app.router.GeoSiteList - (*RoutingRule)(nil), // 8: xray.app.router.RoutingRule - (*BalancingRule)(nil), // 9: xray.app.router.BalancingRule - (*StrategyWeight)(nil), // 10: xray.app.router.StrategyWeight - (*StrategyLeastLoadConfig)(nil), // 11: xray.app.router.StrategyLeastLoadConfig - (*Config)(nil), // 12: xray.app.router.Config - (*Domain_Attribute)(nil), // 13: xray.app.router.Domain.Attribute - nil, // 14: xray.app.router.RoutingRule.AttributesEntry - (*net.PortList)(nil), // 15: xray.common.net.PortList - (net.Network)(0), // 16: xray.common.net.Network - (*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage +var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_app_router_config_proto_goTypes = []interface{}{ + (Domain_Type)(0), // 0: xray.app.router.Domain.Type + (Config_DomainStrategy)(0), // 1: xray.app.router.Config.DomainStrategy + (*Domain)(nil), // 2: xray.app.router.Domain + (*CIDR)(nil), // 3: xray.app.router.CIDR + (*GeoIP)(nil), // 4: xray.app.router.GeoIP + (*GeoIPList)(nil), // 5: xray.app.router.GeoIPList + (*GeoSite)(nil), // 6: xray.app.router.GeoSite + (*GeoSiteList)(nil), // 7: xray.app.router.GeoSiteList + (*RoutingRule)(nil), // 8: xray.app.router.RoutingRule + (*BalancingRule)(nil), // 9: xray.app.router.BalancingRule + (*Config)(nil), // 10: xray.app.router.Config + (*Domain_Attribute)(nil), // 11: xray.app.router.Domain.Attribute + (*net.PortRange)(nil), // 12: xray.common.net.PortRange + (*net.PortList)(nil), // 13: xray.common.net.PortList + (*net.NetworkList)(nil), // 14: xray.common.net.NetworkList + (net.Network)(0), // 15: xray.common.net.Network } var file_app_router_config_proto_depIdxs = []int32{ 0, // 0: xray.app.router.Domain.type:type_name -> xray.app.router.Domain.Type - 13, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute + 11, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute 3, // 2: xray.app.router.GeoIP.cidr:type_name -> xray.app.router.CIDR 4, // 3: xray.app.router.GeoIPList.entry:type_name -> xray.app.router.GeoIP 2, // 4: xray.app.router.GeoSite.domain:type_name -> xray.app.router.Domain 6, // 5: xray.app.router.GeoSiteList.entry:type_name -> xray.app.router.GeoSite 2, // 6: xray.app.router.RoutingRule.domain:type_name -> xray.app.router.Domain - 4, // 7: xray.app.router.RoutingRule.geoip:type_name -> xray.app.router.GeoIP - 15, // 8: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList - 16, // 9: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network - 4, // 10: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP - 15, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList - 14, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry - 17, // 13: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage - 10, // 14: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight - 1, // 15: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy - 8, // 16: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule - 9, // 17: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule - 18, // [18:18] is the sub-list for method output_type - 18, // [18:18] is the sub-list for method input_type - 18, // [18:18] is the sub-list for extension type_name - 18, // [18:18] is the sub-list for extension extendee - 0, // [0:18] is the sub-list for field type_name + 3, // 7: xray.app.router.RoutingRule.cidr:type_name -> xray.app.router.CIDR + 4, // 8: xray.app.router.RoutingRule.geoip:type_name -> xray.app.router.GeoIP + 12, // 9: xray.app.router.RoutingRule.port_range:type_name -> xray.common.net.PortRange + 13, // 10: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList + 14, // 11: xray.app.router.RoutingRule.network_list:type_name -> xray.common.net.NetworkList + 15, // 12: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network + 3, // 13: xray.app.router.RoutingRule.source_cidr:type_name -> xray.app.router.CIDR + 4, // 14: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP + 13, // 15: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList + 1, // 16: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy + 8, // 17: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule + 9, // 18: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule + 19, // [19:19] is the sub-list for method output_type + 19, // [19:19] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_app_router_config_proto_init() } @@ -1237,11 +1118,133 @@ func file_app_router_config_proto_init() { if File_app_router_config_proto != nil { return } - file_app_router_config_proto_msgTypes[6].OneofWrappers = []any{ + if !protoimpl.UnsafeEnabled { + file_app_router_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Domain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CIDR); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoIP); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoIPList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoSite); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoSiteList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoutingRule); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BalancingRule); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_router_config_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Domain_Attribute); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_app_router_config_proto_msgTypes[6].OneofWrappers = []interface{}{ (*RoutingRule_Tag)(nil), (*RoutingRule_BalancingTag)(nil), } - file_app_router_config_proto_msgTypes[11].OneofWrappers = []any{ + file_app_router_config_proto_msgTypes[9].OneofWrappers = []interface{}{ (*Domain_Attribute_BoolValue)(nil), (*Domain_Attribute_IntValue)(nil), } @@ -1251,7 +1254,7 @@ func file_app_router_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_router_config_proto_rawDesc, NumEnums: 2, - NumMessages: 13, + NumMessages: 10, NumExtensions: 0, NumServices: 0, }, diff --git a/app/router/config.proto b/app/router/config.proto index 505f0712..2886077a 100644 --- a/app/router/config.proto +++ b/app/router/config.proto @@ -6,7 +6,6 @@ option go_package = "github.com/xtls/xray-core/app/router"; option java_package = "com.xray.app.router"; option java_multiple_files = true; -import "common/serial/typed_message.proto"; import "common/net/port.proto"; import "common/net/network.proto"; @@ -79,23 +78,36 @@ message RoutingRule { // Tag of routing balancer. string balancing_tag = 12; } - string rule_tag = 18; // List of domains for target domain matching. repeated Domain domain = 2; + // List of CIDRs for target IP address matching. + // Deprecated. Use geoip below. + repeated CIDR cidr = 3 [deprecated = true]; + // List of GeoIPs for target IP address matching. If this entry exists, the // 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; + // A range of port [from, to]. If the destination port is in this range, this + // rule takes effect. Deprecated. Use port_list. + xray.common.net.PortRange port_range = 4 [deprecated = true]; + // List of ports. xray.common.net.PortList port_list = 14; + // List of networks. Deprecated. Use networks. + xray.common.net.NetworkList network_list = 5 [deprecated = true]; + // List of networks for matching. repeated xray.common.net.Network networks = 13; + // List of CIDRs for source IP address matching. + repeated CIDR source_cidr = 6 [deprecated = true]; + // List of GeoIPs for source IP address matching. If this entry exists, the // source_cidr above will have no effect. repeated GeoIP source_geoip = 11; @@ -107,7 +119,7 @@ message RoutingRule { repeated string inbound_tag = 8; repeated string protocol = 9; - map attributes = 15; + string attributes = 15; string domain_matcher = 17; } @@ -116,27 +128,6 @@ message BalancingRule { string tag = 1; repeated string outbound_selector = 2; string strategy = 3; - xray.common.serial.TypedMessage strategy_settings = 4; - string fallback_tag = 5; -} - -message StrategyWeight { - bool regexp = 1; - string match = 2; - float value =3; -} - -message StrategyLeastLoadConfig { - // weight settings - repeated StrategyWeight costs = 2; - // RTT baselines for selecting, int64 values of time.Duration - repeated int64 baselines = 3; - // expected nodes count to select - int32 expected = 4; - // max acceptable rtt, filter away high delay nodes. default 0 - int64 maxRTT = 5; - // acceptable failure rate - float tolerance = 6; } message Config { diff --git a/app/router/errors.generated.go b/app/router/errors.generated.go new file mode 100644 index 00000000..1e5d74ba --- /dev/null +++ b/app/router/errors.generated.go @@ -0,0 +1,9 @@ +package router + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/router/router.go b/app/router/router.go index 2f35b3e7..010f8d0a 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -1,12 +1,11 @@ package router +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - sync "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/outbound" @@ -20,11 +19,6 @@ type Router struct { rules []*Rule balancers map[string]*Balancer dns dns.Client - - ctx context.Context - ohm outbound.Manager - dispatcher routing.Dispatcher - mu sync.Mutex } // Route is an implementation of routing.Route. @@ -32,20 +26,16 @@ type Route struct { routing.Context outboundGroupTags []string outboundTag string - ruleTag string } // Init initializes the Router. -func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error { +func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm outbound.Manager) error { r.domainStrategy = config.DomainStrategy r.dns = d - r.ctx = ctx - r.ohm = ohm - r.dispatcher = dispatcher r.balancers = make(map[string]*Balancer, len(config.BalancingRule)) for _, rule := range config.BalancingRule { - balancer, err := rule.Build(ohm, dispatcher) + balancer, err := rule.Build(ohm) if err != nil { return err } @@ -62,13 +52,12 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out rr := &Rule{ Condition: cond, Tag: rule.GetTag(), - RuleTag: rule.GetRuleTag(), } btag := rule.GetBalancingTag() if len(btag) > 0 { brule, found := r.balancers[btag] if !found { - return errors.New("balancer ", btag, " not found") + return newError("balancer ", btag, " not found") } rr.Balancer = brule } @@ -88,99 +77,9 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) { if err != nil { return nil, err } - return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil + return &Route{Context: ctx, outboundTag: tag}, nil } -// AddRule implements routing.Router. -func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error { - - inst, err := config.GetInstance() - if err != nil { - return err - } - if c, ok := inst.(*Config); ok { - return r.ReloadRules(c, shouldAppend) - } - return errors.New("AddRule: config type error") -} - -func (r *Router) ReloadRules(config *Config, shouldAppend bool) error { - r.mu.Lock() - defer r.mu.Unlock() - - if !shouldAppend { - r.balancers = make(map[string]*Balancer, len(config.BalancingRule)) - r.rules = make([]*Rule, 0, len(config.Rule)) - } - for _, rule := range config.BalancingRule { - _, found := r.balancers[rule.Tag] - if found { - return errors.New("duplicate balancer tag") - } - balancer, err := rule.Build(r.ohm, r.dispatcher) - if err != nil { - return err - } - balancer.InjectContext(r.ctx) - r.balancers[rule.Tag] = balancer - } - - for _, rule := range config.Rule { - if r.RuleExists(rule.GetRuleTag()) { - return errors.New("duplicate ruleTag ", rule.GetRuleTag()) - } - cond, err := rule.BuildCondition() - if err != nil { - return err - } - rr := &Rule{ - Condition: cond, - Tag: rule.GetTag(), - RuleTag: rule.GetRuleTag(), - } - btag := rule.GetBalancingTag() - if len(btag) > 0 { - brule, found := r.balancers[btag] - if !found { - return errors.New("balancer ", btag, " not found") - } - rr.Balancer = brule - } - r.rules = append(r.rules, rr) - } - - return nil -} - -func (r *Router) RuleExists(tag string) bool { - if tag != "" { - for _, rule := range r.rules { - if rule.RuleTag == tag { - return true - } - } - } - return false -} - -// RemoveRule implements routing.Router. -func (r *Router) RemoveRule(tag string) error { - r.mu.Lock() - defer r.mu.Unlock() - - newRules := []*Rule{} - if tag != "" { - for _, rule := range r.rules { - if rule.RuleTag != tag { - newRules = append(newRules, rule) - } - } - r.rules = newRules - return nil - } - return errors.New("empty tag name!") - -} func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) { // SkipDNSResolve is set from DNS module. // the DOH remote server maybe a domain name, @@ -214,12 +113,12 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, } // Start implements common.Runnable. -func (r *Router) Start() error { +func (*Router) Start() error { return nil } // Close implements common.Closable. -func (r *Router) Close() error { +func (*Router) Close() error { return nil } @@ -238,15 +137,11 @@ func (r *Route) GetOutboundTag() string { return r.outboundTag } -func (r *Route) GetRuleTag() string { - return r.ruleTag -} - func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { r := new(Router) - if err := core.RequireFeatures(ctx, func(d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error { - return r.Init(ctx, config.(*Config), d, ohm, dispatcher) + if err := core.RequireFeatures(ctx, func(d dns.Client, ohm outbound.Manager) error { + return r.Init(ctx, config.(*Config), d, ohm) }); err != nil { return nil, err } diff --git a/app/router/router_test.go b/app/router/router_test.go index a0516e05..1b71e99b 100644 --- a/app/router/router_test.go +++ b/app/router/router_test.go @@ -43,11 +43,9 @@ func TestSimpleRouter(t *testing.T) { common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{ Manager: mockOhm, HandlerSelector: mockHs, - }, nil)) + })) - ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ - Target: net.TCPDestination(net.DomainAddress("example.com"), 80), - }}) + ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { @@ -86,11 +84,9 @@ func TestSimpleBalancer(t *testing.T) { common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{ Manager: mockOhm, HandlerSelector: mockHs, - }, nil)) + })) - ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ - Target: net.TCPDestination(net.DomainAddress("example.com"), 80), - }}) + ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { @@ -98,55 +94,6 @@ func TestSimpleBalancer(t *testing.T) { } } -/* - -Do not work right now: need a full client setup - -func TestLeastLoadBalancer(t *testing.T) { - config := &Config{ - Rule: []*RoutingRule{ - { - TargetTag: &RoutingRule_BalancingTag{ - BalancingTag: "balance", - }, - Networks: []net.Network{net.Network_TCP}, - }, - }, - BalancingRule: []*BalancingRule{ - { - Tag: "balance", - OutboundSelector: []string{"test-"}, - Strategy: "leastLoad", - StrategySettings: serial.ToTypedMessage(&StrategyLeastLoadConfig{ - Baselines: nil, - Expected: 1, - }), - }, - }, - } - - mockCtl := gomock.NewController(t) - defer mockCtl.Finish() - - mockDNS := mocks.NewDNSClient(mockCtl) - mockOhm := mocks.NewOutboundManager(mockCtl) - mockHs := mocks.NewOutboundHandlerSelector(mockCtl) - - mockHs.EXPECT().Select(gomock.Eq([]string{"test-"})).Return([]string{"test1"}) - - r := new(Router) - common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{ - Manager: mockOhm, - HandlerSelector: mockHs, - }, nil)) - ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)}) - route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) - common.Must(err) - if tag := route.GetOutboundTag(); tag != "test1" { - t.Error("expect tag 'test1', bug actually ", tag) - } -}*/ - func TestIPOnDemand(t *testing.T) { config := &Config{ DomainStrategy: Config_IpOnDemand, @@ -155,14 +102,10 @@ func TestIPOnDemand(t *testing.T) { TargetTag: &RoutingRule_Tag{ Tag: "test", }, - Geoip: []*GeoIP{ + Cidr: []*CIDR{ { - Cidr: []*CIDR{ - { - Ip: []byte{192, 168, 0, 0}, - Prefix: 16, - }, - }, + Ip: []byte{192, 168, 0, 0}, + Prefix: 16, }, }, }, @@ -177,14 +120,12 @@ func TestIPOnDemand(t *testing.T) { IPv4Enable: true, IPv6Enable: true, FakeEnable: false, - }).Return([]net.IP{{192, 168, 0, 1}}, uint32(600), nil).AnyTimes() + }).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() r := new(Router) - common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) + common.Must(r.Init(context.TODO(), config, mockDNS, nil)) - ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ - Target: net.TCPDestination(net.DomainAddress("example.com"), 80), - }}) + ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { @@ -200,14 +141,10 @@ func TestIPIfNonMatchDomain(t *testing.T) { TargetTag: &RoutingRule_Tag{ Tag: "test", }, - Geoip: []*GeoIP{ + Cidr: []*CIDR{ { - Cidr: []*CIDR{ - { - Ip: []byte{192, 168, 0, 0}, - Prefix: 16, - }, - }, + Ip: []byte{192, 168, 0, 0}, + Prefix: 16, }, }, }, @@ -222,14 +159,12 @@ func TestIPIfNonMatchDomain(t *testing.T) { IPv4Enable: true, IPv6Enable: true, FakeEnable: false, - }).Return([]net.IP{{192, 168, 0, 1}}, uint32(600), nil).AnyTimes() + }).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() r := new(Router) - common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) + common.Must(r.Init(context.TODO(), config, mockDNS, nil)) - ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ - Target: net.TCPDestination(net.DomainAddress("example.com"), 80), - }}) + ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { @@ -245,14 +180,10 @@ func TestIPIfNonMatchIP(t *testing.T) { TargetTag: &RoutingRule_Tag{ Tag: "test", }, - Geoip: []*GeoIP{ + Cidr: []*CIDR{ { - Cidr: []*CIDR{ - { - Ip: []byte{127, 0, 0, 0}, - Prefix: 8, - }, - }, + Ip: []byte{127, 0, 0, 0}, + Prefix: 8, }, }, }, @@ -265,11 +196,9 @@ func TestIPIfNonMatchIP(t *testing.T) { mockDNS := mocks.NewDNSClient(mockCtl) r := new(Router) - common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) + common.Must(r.Init(context.TODO(), config, mockDNS, nil)) - ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ - Target: net.TCPDestination(net.LocalHostIP, 80), - }}) + ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { diff --git a/app/router/strategy_leastload.go b/app/router/strategy_leastload.go deleted file mode 100644 index 1bf3cbc0..00000000 --- a/app/router/strategy_leastload.go +++ /dev/null @@ -1,203 +0,0 @@ -package router - -import ( - "context" - "math" - "sort" - "time" - - "github.com/xtls/xray-core/app/observatory" - "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/core" - "github.com/xtls/xray-core/features/extension" -) - -// LeastLoadStrategy represents a least load balancing strategy -type LeastLoadStrategy struct { - settings *StrategyLeastLoadConfig - costs *WeightManager - - observer extension.Observatory - - ctx context.Context -} - -func (l *LeastLoadStrategy) GetPrincipleTarget(strings []string) []string { - var ret []string - nodes := l.pickOutbounds(strings) - for _, v := range nodes { - ret = append(ret, v.Tag) - } - return ret -} - -// NewLeastLoadStrategy creates a new LeastLoadStrategy with settings -func NewLeastLoadStrategy(settings *StrategyLeastLoadConfig) *LeastLoadStrategy { - return &LeastLoadStrategy{ - settings: settings, - costs: NewWeightManager( - settings.Costs, 1, - func(value, cost float64) float64 { - return value * math.Pow(cost, 0.5) - }, - ), - } -} - -// node is a minimal copy of HealthCheckResult -// we don't use HealthCheckResult directly because -// it may change by health checker during routing -type node struct { - Tag string - CountAll int - CountFail int - RTTAverage time.Duration - RTTDeviation time.Duration - RTTDeviationCost time.Duration -} - -func (s *LeastLoadStrategy) InjectContext(ctx context.Context) { - s.ctx = ctx - common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error { - s.observer = observatory - return nil - })) -} - -func (s *LeastLoadStrategy) PickOutbound(candidates []string) string { - selects := s.pickOutbounds(candidates) - count := len(selects) - if count == 0 { - // goes to fallbackTag - return "" - } - return selects[dice.Roll(count)].Tag -} - -func (s *LeastLoadStrategy) pickOutbounds(candidates []string) []*node { - qualified := s.getNodes(candidates, time.Duration(s.settings.MaxRTT)) - selects := s.selectLeastLoad(qualified) - return selects -} - -// selectLeastLoad selects nodes according to Baselines and Expected Count. -// -// The strategy always improves network response speed, not matter which mode below is configured. -// But they can still have different priorities. -// -// 1. Bandwidth priority: no Baseline + Expected Count > 0.: selects `Expected Count` of nodes. -// (one if Expected Count <= 0) -// -// 2. Bandwidth priority advanced: Baselines + Expected Count > 0. -// Select `Expected Count` amount of nodes, and also those near them according to baselines. -// In other words, it selects according to different Baselines, until one of them matches -// the Expected Count, if no Baseline matches, Expected Count applied. -// -// 3. Speed priority: Baselines + `Expected Count <= 0`. -// go through all baselines until find selects, if not, select none. Used in combination -// with 'balancer.fallbackTag', it means: selects qualified nodes or use the fallback. -func (s *LeastLoadStrategy) selectLeastLoad(nodes []*node) []*node { - if len(nodes) == 0 { - errors.LogInfo(s.ctx, "least load: no qualified outbound") - return nil - } - expected := int(s.settings.Expected) - availableCount := len(nodes) - if expected > availableCount { - return nodes - } - - if expected <= 0 { - expected = 1 - } - if len(s.settings.Baselines) == 0 { - return nodes[:expected] - } - - count := 0 - // go through all base line until find expected selects - for _, b := range s.settings.Baselines { - baseline := time.Duration(b) - for i := count; i < availableCount; i++ { - if nodes[i].RTTDeviationCost >= baseline { - break - } - count = i + 1 - } - // don't continue if find expected selects - if count >= expected { - errors.LogDebug(s.ctx, "applied baseline: ", baseline) - break - } - } - if s.settings.Expected > 0 && count < expected { - count = expected - } - return nodes[:count] -} - -func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration) []*node { - if s.observer == nil { - errors.LogError(s.ctx, "observer is nil") - return make([]*node, 0) - } - observeResult, err := s.observer.GetObservation(s.ctx) - if err != nil { - errors.LogInfoInner(s.ctx, err, "cannot get observation") - return make([]*node, 0) - } - - results := observeResult.(*observatory.ObservationResult) - - outboundlist := outboundList(candidates) - - var ret []*node - - for _, v := range results.Status { - if v.Alive && (v.Delay < maxRTT.Milliseconds() || maxRTT == 0) && outboundlist.contains(v.OutboundTag) { - record := &node{ - Tag: v.OutboundTag, - CountAll: 1, - CountFail: 1, - RTTAverage: time.Duration(v.Delay) * time.Millisecond, - RTTDeviation: time.Duration(v.Delay) * time.Millisecond, - RTTDeviationCost: time.Duration(s.costs.Apply(v.OutboundTag, float64(time.Duration(v.Delay)*time.Millisecond))), - } - - if v.HealthPing != nil { - record.RTTAverage = time.Duration(v.HealthPing.Average) - record.RTTDeviation = time.Duration(v.HealthPing.Deviation) - record.RTTDeviationCost = time.Duration(s.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation))) - record.CountAll = int(v.HealthPing.All) - record.CountFail = int(v.HealthPing.Fail) - - } - ret = append(ret, record) - } - } - - leastloadSort(ret) - return ret -} - -func leastloadSort(nodes []*node) { - sort.Slice(nodes, func(i, j int) bool { - left := nodes[i] - right := nodes[j] - if left.RTTDeviationCost != right.RTTDeviationCost { - return left.RTTDeviationCost < right.RTTDeviationCost - } - if left.RTTAverage != right.RTTAverage { - return left.RTTAverage < right.RTTAverage - } - if left.CountFail != right.CountFail { - return left.CountFail < right.CountFail - } - if left.CountAll != right.CountAll { - return left.CountAll > right.CountAll - } - return left.Tag < right.Tag - }) -} diff --git a/app/router/strategy_leastload_test.go b/app/router/strategy_leastload_test.go deleted file mode 100644 index 832e0a87..00000000 --- a/app/router/strategy_leastload_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package router - -import ( - "testing" -) - -/* -Split into multiple package, need to be tested separately - - func TestSelectLeastLoad(t *testing.T) { - settings := &StrategyLeastLoadConfig{ - HealthCheck: &HealthPingConfig{ - SamplingCount: 10, - }, - Expected: 1, - MaxRTT: int64(time.Millisecond * time.Duration(800)), - } - strategy := NewLeastLoadStrategy(settings) - // std 40 - strategy.PutResult("a", time.Millisecond*time.Duration(60)) - strategy.PutResult("a", time.Millisecond*time.Duration(140)) - strategy.PutResult("a", time.Millisecond*time.Duration(60)) - strategy.PutResult("a", time.Millisecond*time.Duration(140)) - // std 60 - strategy.PutResult("b", time.Millisecond*time.Duration(40)) - strategy.PutResult("b", time.Millisecond*time.Duration(160)) - strategy.PutResult("b", time.Millisecond*time.Duration(40)) - strategy.PutResult("b", time.Millisecond*time.Duration(160)) - // std 0, but >MaxRTT - strategy.PutResult("c", time.Millisecond*time.Duration(1000)) - strategy.PutResult("c", time.Millisecond*time.Duration(1000)) - strategy.PutResult("c", time.Millisecond*time.Duration(1000)) - strategy.PutResult("c", time.Millisecond*time.Duration(1000)) - expected := "a" - actual := strategy.SelectAndPick([]string{"a", "b", "c", "untested"}) - if actual != expected { - t.Errorf("expected: %v, actual: %v", expected, actual) - } - } - - func TestSelectLeastLoadWithCost(t *testing.T) { - settings := &StrategyLeastLoadConfig{ - HealthCheck: &HealthPingConfig{ - SamplingCount: 10, - }, - Costs: []*StrategyWeight{ - {Match: "a", Value: 9}, - }, - Expected: 1, - } - strategy := NewLeastLoadStrategy(settings, nil) - // std 40, std+c 120 - strategy.PutResult("a", time.Millisecond*time.Duration(60)) - strategy.PutResult("a", time.Millisecond*time.Duration(140)) - strategy.PutResult("a", time.Millisecond*time.Duration(60)) - strategy.PutResult("a", time.Millisecond*time.Duration(140)) - // std 60 - strategy.PutResult("b", time.Millisecond*time.Duration(40)) - strategy.PutResult("b", time.Millisecond*time.Duration(160)) - strategy.PutResult("b", time.Millisecond*time.Duration(40)) - strategy.PutResult("b", time.Millisecond*time.Duration(160)) - expected := "b" - actual := strategy.SelectAndPick([]string{"a", "b", "untested"}) - if actual != expected { - t.Errorf("expected: %v, actual: %v", expected, actual) - } - } -*/ -func TestSelectLeastExpected(t *testing.T) { - strategy := &LeastLoadStrategy{ - settings: &StrategyLeastLoadConfig{ - Baselines: nil, - Expected: 3, - }, - } - nodes := []*node{ - {Tag: "a", RTTDeviationCost: 100}, - {Tag: "b", RTTDeviationCost: 200}, - {Tag: "c", RTTDeviationCost: 300}, - {Tag: "d", RTTDeviationCost: 350}, - } - expected := 3 - ns := strategy.selectLeastLoad(nodes) - if len(ns) != expected { - t.Errorf("expected: %v, actual: %v", expected, len(ns)) - } -} -func TestSelectLeastExpected2(t *testing.T) { - strategy := &LeastLoadStrategy{ - settings: &StrategyLeastLoadConfig{ - Baselines: nil, - Expected: 3, - }, - } - nodes := []*node{ - {Tag: "a", RTTDeviationCost: 100}, - {Tag: "b", RTTDeviationCost: 200}, - } - expected := 2 - ns := strategy.selectLeastLoad(nodes) - if len(ns) != expected { - t.Errorf("expected: %v, actual: %v", expected, len(ns)) - } -} -func TestSelectLeastExpectedAndBaselines(t *testing.T) { - strategy := &LeastLoadStrategy{ - settings: &StrategyLeastLoadConfig{ - Baselines: []int64{200, 300, 400}, - Expected: 3, - }, - } - nodes := []*node{ - {Tag: "a", RTTDeviationCost: 100}, - {Tag: "b", RTTDeviationCost: 200}, - {Tag: "c", RTTDeviationCost: 250}, - {Tag: "d", RTTDeviationCost: 300}, - {Tag: "e", RTTDeviationCost: 310}, - } - expected := 3 - ns := strategy.selectLeastLoad(nodes) - if len(ns) != expected { - t.Errorf("expected: %v, actual: %v", expected, len(ns)) - } -} -func TestSelectLeastExpectedAndBaselines2(t *testing.T) { - strategy := &LeastLoadStrategy{ - settings: &StrategyLeastLoadConfig{ - Baselines: []int64{200, 300, 400}, - Expected: 3, - }, - } - nodes := []*node{ - {Tag: "a", RTTDeviationCost: 500}, - {Tag: "b", RTTDeviationCost: 600}, - {Tag: "c", RTTDeviationCost: 700}, - {Tag: "d", RTTDeviationCost: 800}, - {Tag: "e", RTTDeviationCost: 900}, - } - expected := 3 - ns := strategy.selectLeastLoad(nodes) - if len(ns) != expected { - t.Errorf("expected: %v, actual: %v", expected, len(ns)) - } -} -func TestSelectLeastLoadBaselines(t *testing.T) { - strategy := &LeastLoadStrategy{ - settings: &StrategyLeastLoadConfig{ - Baselines: []int64{200, 400, 600}, - Expected: 0, - }, - } - nodes := []*node{ - {Tag: "a", RTTDeviationCost: 100}, - {Tag: "b", RTTDeviationCost: 200}, - {Tag: "c", RTTDeviationCost: 300}, - } - expected := 1 - ns := strategy.selectLeastLoad(nodes) - if len(ns) != expected { - t.Errorf("expected: %v, actual: %v", expected, len(ns)) - } -} -func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) { - strategy := &LeastLoadStrategy{ - settings: &StrategyLeastLoadConfig{ - Baselines: []int64{200, 400, 600}, - Expected: 0, - }, - } - nodes := []*node{ - {Tag: "a", RTTDeviationCost: 800}, - {Tag: "b", RTTDeviationCost: 1000}, - } - expected := 0 - ns := strategy.selectLeastLoad(nodes) - if len(ns) != expected { - t.Errorf("expected: %v, actual: %v", expected, len(ns)) - } -} diff --git a/app/router/strategy_leastping.go b/app/router/strategy_leastping.go index ada3492d..e8c40aea 100644 --- a/app/router/strategy_leastping.go +++ b/app/router/strategy_leastping.go @@ -5,7 +5,6 @@ import ( "github.com/xtls/xray-core/app/observatory" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/extension" ) @@ -15,26 +14,21 @@ type LeastPingStrategy struct { observatory extension.Observatory } -func (l *LeastPingStrategy) GetPrincipleTarget(strings []string) []string { - return []string{l.PickOutbound(strings)} -} - func (l *LeastPingStrategy) InjectContext(ctx context.Context) { l.ctx = ctx - common.Must(core.RequireFeatures(l.ctx, func(observatory extension.Observatory) error { - l.observatory = observatory - return nil - })) } func (l *LeastPingStrategy) PickOutbound(strings []string) string { if l.observatory == nil { - errors.LogError(l.ctx, "observer is nil") - return "" + common.Must(core.RequireFeatures(l.ctx, func(observatory extension.Observatory) error { + l.observatory = observatory + return nil + })) } + observeReport, err := l.observatory.GetObservation(l.ctx) if err != nil { - errors.LogInfoInner(l.ctx, err, "cannot get observer report") + newError("cannot get observe report").Base(err).WriteToLog() return "" } outboundsList := outboundList(strings) diff --git a/app/router/strategy_random.go b/app/router/strategy_random.go deleted file mode 100644 index ea9b7add..00000000 --- a/app/router/strategy_random.go +++ /dev/null @@ -1,67 +0,0 @@ -package router - -import ( - "context" - - "github.com/xtls/xray-core/app/observatory" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/features/extension" -) - -// RandomStrategy represents a random balancing strategy -type RandomStrategy struct { - FallbackTag string - - ctx context.Context - observatory extension.Observatory -} - -func (s *RandomStrategy) InjectContext(ctx context.Context) { - s.ctx = ctx - if len(s.FallbackTag) > 0 { - common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error { - s.observatory = observatory - return nil - })) - } -} - -func (s *RandomStrategy) GetPrincipleTarget(strings []string) []string { - return strings -} - -func (s *RandomStrategy) PickOutbound(candidates []string) string { - if s.observatory != nil { - observeReport, err := s.observatory.GetObservation(s.ctx) - if err == nil { - aliveTags := make([]string, 0) - if result, ok := observeReport.(*observatory.ObservationResult); ok { - status := result.Status - statusMap := make(map[string]*observatory.OutboundStatus) - for _, outboundStatus := range status { - statusMap[outboundStatus.OutboundTag] = outboundStatus - } - for _, candidate := range candidates { - if outboundStatus, found := statusMap[candidate]; found { - if outboundStatus.Alive { - aliveTags = append(aliveTags, candidate) - } - } else { - // unfound candidate is considered alive - aliveTags = append(aliveTags, candidate) - } - } - candidates = aliveTags - } - } - } - - count := len(candidates) - if count == 0 { - // goes to fallbackTag - return "" - } - return candidates[dice.Roll(count)] -} diff --git a/app/router/weight.go b/app/router/weight.go deleted file mode 100644 index 1bba645e..00000000 --- a/app/router/weight.go +++ /dev/null @@ -1,92 +0,0 @@ -package router - -import ( - "context" - "regexp" - "strconv" - "strings" - "sync" - - "github.com/xtls/xray-core/common/errors" -) - -type weightScaler func(value, weight float64) float64 - -var numberFinder = regexp.MustCompile(`\d+(\.\d+)?`) - -// NewWeightManager creates a new WeightManager with settings -func NewWeightManager(s []*StrategyWeight, defaultWeight float64, scaler weightScaler) *WeightManager { - return &WeightManager{ - settings: s, - cache: make(map[string]float64), - scaler: scaler, - defaultWeight: defaultWeight, - } -} - -// WeightManager manages weights for specific settings -type WeightManager struct { - settings []*StrategyWeight - cache map[string]float64 - scaler weightScaler - defaultWeight float64 - mu sync.Mutex -} - -// Get get the weight of specified tag -func (s *WeightManager) Get(tag string) float64 { - s.mu.Lock() - defer s.mu.Unlock() - weight, ok := s.cache[tag] - if ok { - return weight - } - weight = s.findValue(tag) - s.cache[tag] = weight - return weight -} - -// Apply applies weight to the value -func (s *WeightManager) Apply(tag string, value float64) float64 { - return s.scaler(value, s.Get(tag)) -} - -func (s *WeightManager) findValue(tag string) float64 { - for _, w := range s.settings { - matched := s.getMatch(tag, w.Match, w.Regexp) - if matched == "" { - continue - } - if w.Value > 0 { - return float64(w.Value) - } - // auto weight from matched - numStr := numberFinder.FindString(matched) - if numStr == "" { - return s.defaultWeight - } - weight, err := strconv.ParseFloat(numStr, 64) - if err != nil { - errors.LogError(context.Background(), "unexpected error from ParseFloat: ", err) - return s.defaultWeight - } - return weight - } - return s.defaultWeight -} - -func (s *WeightManager) getMatch(tag, find string, isRegexp bool) string { - if !isRegexp { - idx := strings.Index(tag, find) - if idx < 0 { - return "" - } - return find - } - r, err := regexp.Compile(find) - if err != nil { - errors.LogError(context.Background(), "invalid regexp: ", find, "err: ", err) - return "" - } - return r.FindString(tag) -} diff --git a/app/router/weight_test.go b/app/router/weight_test.go deleted file mode 100644 index 2e6a91e5..00000000 --- a/app/router/weight_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package router_test - -import ( - "reflect" - "testing" - - "github.com/xtls/xray-core/app/router" -) - -func TestWeight(t *testing.T) { - manager := router.NewWeightManager( - []*router.StrategyWeight{ - { - Match: "x5", - Value: 100, - }, - { - Match: "x8", - }, - { - Regexp: true, - Match: `\bx0+(\.\d+)?\b`, - Value: 1, - }, - { - Regexp: true, - Match: `\bx\d+(\.\d+)?\b`, - }, - }, - 1, func(v, w float64) float64 { - return v * w - }, - ) - tags := []string{ - "node name, x5, and more", - "node name, x8", - "node name, x15", - "node name, x0100, and more", - "node name, x10.1", - "node name, x00.1, and more", - } - // test weight - expected := []float64{100, 8, 15, 100, 10.1, 1} - actual := make([]float64, 0) - for _, tag := range tags { - actual = append(actual, manager.Get(tag)) - } - if !reflect.DeepEqual(expected, actual) { - t.Errorf("expected: %v, actual: %v", expected, actual) - } - // test scale - expected2 := []float64{1000, 80, 150, 1000, 101, 10} - actual2 := make([]float64, 0) - for _, tag := range tags { - actual2 = append(actual2, manager.Apply(tag, 10)) - } - if !reflect.DeepEqual(expected2, actual2) { - t.Errorf("expected2: %v, actual2: %v", expected2, actual2) - } -} diff --git a/app/stats/channel.go b/app/stats/channel.go index b32850d3..99f3890c 100644 --- a/app/stats/channel.go +++ b/app/stats/channel.go @@ -5,7 +5,6 @@ import ( "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" ) // Channel is an implementation of stats.Channel. @@ -45,7 +44,7 @@ func (c *Channel) Subscribe() (chan interface{}, error) { c.access.Lock() defer c.access.Unlock() if c.subsLimit > 0 && len(c.subscribers) >= c.subsLimit { - return nil, errors.New("Number of subscribers has reached limit") + return nil, newError("Number of subscribers has reached limit") } subscriber := make(chan interface{}, c.bufferSize) c.subscribers = append(c.subscribers, subscriber) diff --git a/app/stats/channel_test.go b/app/stats/channel_test.go index f35b95c6..75764f0b 100644 --- a/app/stats/channel_test.go +++ b/app/stats/channel_test.go @@ -95,7 +95,7 @@ func TestStatsChannel(t *testing.T) { } } -func TestStatsChannelUnsubscribe(t *testing.T) { +func TestStatsChannelUnsubcribe(t *testing.T) { c := NewChannel(&ChannelConfig{Blocking: true}) common.Must(c.Start()) defer c.Close() @@ -203,7 +203,7 @@ func TestStatsChannelBlocking(t *testing.T) { // Test blocking channel publishing go func() { - // Dummy message with no subscriber receiving, will block broadcasting goroutine + // Dummy messsage with no subscriber receiving, will block broadcasting goroutine c.Publish(context.Background(), nil) <-pauseCh diff --git a/app/stats/command/command.go b/app/stats/command/command.go index 64e31d38..24dc3db6 100644 --- a/app/stats/command/command.go +++ b/app/stats/command/command.go @@ -1,5 +1,7 @@ package command +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "runtime" @@ -7,7 +9,6 @@ import ( "github.com/xtls/xray-core/app/stats" "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" feature_stats "github.com/xtls/xray-core/features/stats" @@ -30,7 +31,7 @@ func NewStatsServer(manager feature_stats.Manager) StatsServiceServer { func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { c := s.stats.GetCounter(request.Name) if c == nil { - return nil, errors.New(request.Name, " not found.") + return nil, newError(request.Name, " not found.") } var value int64 if request.Reset_ { @@ -46,38 +47,6 @@ func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (* }, nil } -func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { - c := s.stats.GetOnlineMap(request.Name) - if c == nil { - return nil, errors.New(request.Name, " not found.") - } - value := int64(c.Count()) - return &GetStatsResponse{ - Stat: &Stat{ - Name: request.Name, - Value: value, - }, - }, nil -} - -func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) { - c := s.stats.GetOnlineMap(request.Name) - - if c == nil { - return nil, errors.New(request.Name, " not found.") - } - - ips := make(map[string]int64) - for ip, t := range c.IpTimeMap() { - ips[ip] = t.Unix() - } - - return &GetStatsOnlineIpListResponse{ - Name: request.Name, - Ips: ips, - }, nil -} - func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) { matcher, err := strmatcher.Substr.New(request.Pattern) if err != nil { @@ -88,7 +57,7 @@ func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest manager, ok := s.stats.(*stats.Manager) if !ok { - return nil, errors.New("QueryStats only works its own stats.Manager.") + return nil, newError("QueryStats only works its own stats.Manager.") } manager.VisitCounters(func(name string, c feature_stats.Counter) bool { diff --git a/app/stats/command/command.pb.go b/app/stats/command/command.pb.go index 062c2d28..005722b6 100644 --- a/app/stats/command/command.pb.go +++ b/app/stats/command/command.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/stats/command/command.proto package command @@ -33,9 +33,11 @@ type GetStatsRequest struct { func (x *GetStatsRequest) Reset() { *x = GetStatsRequest{} - mi := &file_app_stats_command_command_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GetStatsRequest) String() string { @@ -46,7 +48,7 @@ func (*GetStatsRequest) ProtoMessage() {} func (x *GetStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -86,9 +88,11 @@ type Stat struct { func (x *Stat) Reset() { *x = Stat{} - mi := &file_app_stats_command_command_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Stat) String() string { @@ -99,7 +103,7 @@ func (*Stat) ProtoMessage() {} func (x *Stat) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -138,9 +142,11 @@ type GetStatsResponse struct { func (x *GetStatsResponse) Reset() { *x = GetStatsResponse{} - mi := &file_app_stats_command_command_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *GetStatsResponse) String() string { @@ -151,7 +157,7 @@ func (*GetStatsResponse) ProtoMessage() {} func (x *GetStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -184,9 +190,11 @@ type QueryStatsRequest struct { func (x *QueryStatsRequest) Reset() { *x = QueryStatsRequest{} - mi := &file_app_stats_command_command_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *QueryStatsRequest) String() string { @@ -197,7 +205,7 @@ func (*QueryStatsRequest) ProtoMessage() {} func (x *QueryStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -236,9 +244,11 @@ type QueryStatsResponse struct { func (x *QueryStatsResponse) Reset() { *x = QueryStatsResponse{} - mi := &file_app_stats_command_command_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *QueryStatsResponse) String() string { @@ -249,7 +259,7 @@ func (*QueryStatsResponse) ProtoMessage() {} func (x *QueryStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -279,9 +289,11 @@ type SysStatsRequest struct { func (x *SysStatsRequest) Reset() { *x = SysStatsRequest{} - mi := &file_app_stats_command_command_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SysStatsRequest) String() string { @@ -292,7 +304,7 @@ func (*SysStatsRequest) ProtoMessage() {} func (x *SysStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -326,9 +338,11 @@ type SysStatsResponse struct { func (x *SysStatsResponse) Reset() { *x = SysStatsResponse{} - mi := &file_app_stats_command_command_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SysStatsResponse) String() string { @@ -339,7 +353,7 @@ func (*SysStatsResponse) ProtoMessage() {} func (x *SysStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -424,59 +438,6 @@ func (x *SysStatsResponse) GetUptime() uint32 { return 0 } -type GetStatsOnlineIpListResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Ips map[string]int64 `protobuf:"bytes,2,rep,name=ips,proto3" json:"ips,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` -} - -func (x *GetStatsOnlineIpListResponse) Reset() { - *x = GetStatsOnlineIpListResponse{} - mi := &file_app_stats_command_command_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetStatsOnlineIpListResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetStatsOnlineIpListResponse) ProtoMessage() {} - -func (x *GetStatsOnlineIpListResponse) ProtoReflect() protoreflect.Message { - mi := &file_app_stats_command_command_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetStatsOnlineIpListResponse.ProtoReflect.Descriptor instead. -func (*GetStatsOnlineIpListResponse) Descriptor() ([]byte, []int) { - return file_app_stats_command_command_proto_rawDescGZIP(), []int{7} -} - -func (x *GetStatsOnlineIpListResponse) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetStatsOnlineIpListResponse) GetIps() map[string]int64 { - if x != nil { - return x.Ips - } - return nil -} - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -485,9 +446,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_stats_command_command_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_command_command_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -497,8 +460,8 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_app_stats_command_command_proto_msgTypes[8] - if x != nil { + mi := &file_app_stats_command_command_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -510,7 +473,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_app_stats_command_command_proto_rawDescGZIP(), []int{8} + return file_app_stats_command_command_proto_rawDescGZIP(), []int{7} } var File_app_stats_command_command_proto protoreflect.FileDescriptor @@ -559,60 +522,34 @@ var file_app_stats_command_command_proto_rawDesc = []byte{ 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x50, 0x61, 0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x70, 0x74, - 0x69, 0x6d, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, - 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x70, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x69, 0x70, 0x73, 0x1a, 0x36, 0x0a, 0x08, 0x49, 0x70, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0x9a, 0x04, 0x0a, 0x0c, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08, - 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, - 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, - 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, - 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, - 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x69, 0x6d, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xba, 0x02, + 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, + 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, + 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x47, - 0x65, 0x74, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, - 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x77, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, - 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x34, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, - 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2b, 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, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, + 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, + 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, + 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, + 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2b, 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, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, + 0x70, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -627,38 +564,31 @@ func file_app_stats_command_command_proto_rawDescGZIP() []byte { return file_app_stats_command_command_proto_rawDescData } -var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 10) -var file_app_stats_command_command_proto_goTypes = []any{ - (*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest - (*Stat)(nil), // 1: xray.app.stats.command.Stat - (*GetStatsResponse)(nil), // 2: xray.app.stats.command.GetStatsResponse - (*QueryStatsRequest)(nil), // 3: xray.app.stats.command.QueryStatsRequest - (*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse - (*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest - (*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse - (*GetStatsOnlineIpListResponse)(nil), // 7: xray.app.stats.command.GetStatsOnlineIpListResponse - (*Config)(nil), // 8: xray.app.stats.command.Config - nil, // 9: xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry +var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_app_stats_command_command_proto_goTypes = []interface{}{ + (*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest + (*Stat)(nil), // 1: xray.app.stats.command.Stat + (*GetStatsResponse)(nil), // 2: xray.app.stats.command.GetStatsResponse + (*QueryStatsRequest)(nil), // 3: xray.app.stats.command.QueryStatsRequest + (*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse + (*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest + (*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse + (*Config)(nil), // 7: xray.app.stats.command.Config } var file_app_stats_command_command_proto_depIdxs = []int32{ 1, // 0: xray.app.stats.command.GetStatsResponse.stat:type_name -> xray.app.stats.command.Stat 1, // 1: xray.app.stats.command.QueryStatsResponse.stat:type_name -> xray.app.stats.command.Stat - 9, // 2: xray.app.stats.command.GetStatsOnlineIpListResponse.ips:type_name -> xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry - 0, // 3: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest - 0, // 4: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest - 3, // 5: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest - 5, // 6: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest - 0, // 7: xray.app.stats.command.StatsService.GetStatsOnlineIpList:input_type -> xray.app.stats.command.GetStatsRequest - 2, // 8: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse - 2, // 9: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse - 4, // 10: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse - 6, // 11: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse - 7, // 12: xray.app.stats.command.StatsService.GetStatsOnlineIpList:output_type -> xray.app.stats.command.GetStatsOnlineIpListResponse - 8, // [8:13] is the sub-list for method output_type - 3, // [3:8] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 0, // 2: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest + 3, // 3: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest + 5, // 4: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest + 2, // 5: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse + 4, // 6: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse + 6, // 7: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse + 5, // [5:8] is the sub-list for method output_type + 2, // [2:5] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_app_stats_command_command_proto_init() } @@ -666,13 +596,111 @@ func file_app_stats_command_command_proto_init() { if File_app_stats_command_command_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_stats_command_command_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_command_command_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Stat); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_command_command_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_command_command_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryStatsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_command_command_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryStatsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_command_command_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SysStatsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_command_command_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SysStatsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_command_command_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_stats_command_command_proto_rawDesc, NumEnums: 0, - NumMessages: 10, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/app/stats/command/command.proto b/app/stats/command/command.proto index 58ed7371..bd1f9781 100644 --- a/app/stats/command/command.proto +++ b/app/stats/command/command.proto @@ -46,17 +46,10 @@ message SysStatsResponse { uint32 Uptime = 10; } -message GetStatsOnlineIpListResponse { - string name = 1; - map ips = 2; -} - service StatsService { rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {} - rpc GetStatsOnline(GetStatsRequest) returns (GetStatsResponse) {} rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {} rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {} - rpc GetStatsOnlineIpList(GetStatsRequest) returns (GetStatsOnlineIpListResponse) {} } message Config {} diff --git a/app/stats/command/command_grpc.pb.go b/app/stats/command/command_grpc.pb.go index 6f72eadd..38e9d3fb 100644 --- a/app/stats/command/command_grpc.pb.go +++ b/app/stats/command/command_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.2 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.12 // source: app/stats/command/command.proto package command @@ -15,26 +15,16 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - StatsService_GetStats_FullMethodName = "/xray.app.stats.command.StatsService/GetStats" - StatsService_GetStatsOnline_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnline" - StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats" - StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats" - StatsService_GetStatsOnlineIpList_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnlineIpList" -) +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 // StatsServiceClient is the client API for StatsService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type StatsServiceClient interface { GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) - GetStatsOnline(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) - GetStatsOnlineIpList(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsOnlineIpListResponse, error) } type statsServiceClient struct { @@ -46,19 +36,8 @@ func NewStatsServiceClient(cc grpc.ClientConnInterface) StatsServiceClient { } func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetStatsResponse) - err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *statsServiceClient) GetStatsOnline(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetStatsResponse) - err := c.cc.Invoke(ctx, StatsService_GetStatsOnline_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/GetStats", in, out, opts...) if err != nil { return nil, err } @@ -66,9 +45,8 @@ func (c *statsServiceClient) GetStatsOnline(ctx context.Context, in *GetStatsReq } func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(QueryStatsResponse) - err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/QueryStats", in, out, opts...) if err != nil { return nil, err } @@ -76,19 +54,8 @@ func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsReque } func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SysStatsResponse) - err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *statsServiceClient) GetStatsOnlineIpList(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsOnlineIpListResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetStatsOnlineIpListResponse) - err := c.cc.Invoke(ctx, StatsService_GetStatsOnlineIpList_FullMethodName, in, out, cOpts...) + err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/GetSysStats", in, out, opts...) if err != nil { return nil, err } @@ -97,40 +64,28 @@ func (c *statsServiceClient) GetStatsOnlineIpList(ctx context.Context, in *GetSt // StatsServiceServer is the server API for StatsService service. // All implementations must embed UnimplementedStatsServiceServer -// for forward compatibility. +// for forward compatibility type StatsServiceServer interface { GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) - GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) - GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) mustEmbedUnimplementedStatsServiceServer() } -// UnimplementedStatsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedStatsServiceServer struct{} +// UnimplementedStatsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedStatsServiceServer struct { +} func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented") } -func (UnimplementedStatsServiceServer) GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetStatsOnline not implemented") -} func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryStats not implemented") } func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented") } -func (UnimplementedStatsServiceServer) GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetStatsOnlineIpList not implemented") -} func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {} -func (UnimplementedStatsServiceServer) testEmbeddedByValue() {} // UnsafeStatsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to StatsServiceServer will @@ -140,13 +95,6 @@ type UnsafeStatsServiceServer interface { } func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) { - // If the following call pancis, it indicates UnimplementedStatsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&StatsService_ServiceDesc, srv) } @@ -160,7 +108,7 @@ func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: StatsService_GetStats_FullMethodName, + FullMethod: "/xray.app.stats.command.StatsService/GetStats", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest)) @@ -168,24 +116,6 @@ func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } -func _StatsService_GetStatsOnline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetStatsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(StatsServiceServer).GetStatsOnline(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: StatsService_GetStatsOnline_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(StatsServiceServer).GetStatsOnline(ctx, req.(*GetStatsRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _StatsService_QueryStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryStatsRequest) if err := dec(in); err != nil { @@ -196,7 +126,7 @@ func _StatsService_QueryStats_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: StatsService_QueryStats_FullMethodName, + FullMethod: "/xray.app.stats.command.StatsService/QueryStats", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).QueryStats(ctx, req.(*QueryStatsRequest)) @@ -214,7 +144,7 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: StatsService_GetSysStats_FullMethodName, + FullMethod: "/xray.app.stats.command.StatsService/GetSysStats", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).GetSysStats(ctx, req.(*SysStatsRequest)) @@ -222,24 +152,6 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _StatsService_GetStatsOnlineIpList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetStatsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: StatsService_GetStatsOnlineIpList_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, req.(*GetStatsRequest)) - } - return interceptor(ctx, in, info, handler) -} - // StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -251,10 +163,6 @@ var StatsService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetStats", Handler: _StatsService_GetStats_Handler, }, - { - MethodName: "GetStatsOnline", - Handler: _StatsService_GetStatsOnline_Handler, - }, { MethodName: "QueryStats", Handler: _StatsService_QueryStats_Handler, @@ -263,10 +171,6 @@ var StatsService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetSysStats", Handler: _StatsService_GetSysStats_Handler, }, - { - MethodName: "GetStatsOnlineIpList", - Handler: _StatsService_GetStatsOnlineIpList_Handler, - }, }, Streams: []grpc.StreamDesc{}, Metadata: "app/stats/command/command.proto", diff --git a/app/stats/command/errors.generated.go b/app/stats/command/errors.generated.go new file mode 100644 index 00000000..a1305932 --- /dev/null +++ b/app/stats/command/errors.generated.go @@ -0,0 +1,9 @@ +package command + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/stats/config.pb.go b/app/stats/config.pb.go index 8452c2f8..99c573b5 100644 --- a/app/stats/config.pb.go +++ b/app/stats/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: app/stats/config.proto package stats @@ -28,9 +28,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_stats_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -41,7 +43,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_stats_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -68,9 +70,11 @@ type ChannelConfig struct { func (x *ChannelConfig) Reset() { *x = ChannelConfig{} - mi := &file_app_stats_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_app_stats_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ChannelConfig) String() string { @@ -81,7 +85,7 @@ func (*ChannelConfig) ProtoMessage() {} func (x *ChannelConfig) ProtoReflect() protoreflect.Message { mi := &file_app_stats_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -151,7 +155,7 @@ func file_app_stats_config_proto_rawDescGZIP() []byte { } var file_app_stats_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_app_stats_config_proto_goTypes = []any{ +var file_app_stats_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.app.stats.Config (*ChannelConfig)(nil), // 1: xray.app.stats.ChannelConfig } @@ -168,6 +172,32 @@ func file_app_stats_config_proto_init() { if File_app_stats_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_app_stats_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_stats_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChannelConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/app/stats/counter_test.go b/app/stats/counter_test.go index d7086540..81106468 100644 --- a/app/stats/counter_test.go +++ b/app/stats/counter_test.go @@ -18,7 +18,7 @@ func TestStatsCounter(t *testing.T) { common.Must(err) if v := c.Add(1); v != 1 { - t.Fatal("unexpected Add(1) return: ", v, ", wanted ", 1) + t.Fatal("unpexcted Add(1) return: ", v, ", wanted ", 1) } if v := c.Set(0); v != 1 { diff --git a/app/stats/errors.generated.go b/app/stats/errors.generated.go new file mode 100644 index 00000000..c64386f3 --- /dev/null +++ b/app/stats/errors.generated.go @@ -0,0 +1,9 @@ +package stats + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/stats/online_map.go b/app/stats/online_map.go deleted file mode 100644 index 7cc6ac96..00000000 --- a/app/stats/online_map.go +++ /dev/null @@ -1,90 +0,0 @@ -package stats - -import ( - "sync" - "time" -) - -// OnlineMap is an implementation of stats.OnlineMap. -type OnlineMap struct { - value int - ipList map[string]time.Time - access sync.RWMutex - lastCleanup time.Time - cleanupPeriod time.Duration -} - -// NewOnlineMap creates a new instance of OnlineMap. -func NewOnlineMap() *OnlineMap { - return &OnlineMap{ - ipList: make(map[string]time.Time), - lastCleanup: time.Now(), - cleanupPeriod: 10 * time.Second, - } -} - -// Count implements stats.OnlineMap. -func (c *OnlineMap) Count() int { - return c.value -} - -// List implements stats.OnlineMap. -func (c *OnlineMap) List() []string { - return c.GetKeys() -} - -// AddIP implements stats.OnlineMap. -func (c *OnlineMap) AddIP(ip string) { - list := c.ipList - - if ip == "127.0.0.1" { - return - } - c.access.Lock() - if _, ok := list[ip]; !ok { - list[ip] = time.Now() - } - c.access.Unlock() - if time.Since(c.lastCleanup) > c.cleanupPeriod { - list = c.RemoveExpiredIPs(list) - c.lastCleanup = time.Now() - } - - c.value = len(list) - c.ipList = list -} - -func (c *OnlineMap) GetKeys() []string { - c.access.RLock() - defer c.access.RUnlock() - - keys := []string{} - for k := range c.ipList { - keys = append(keys, k) - } - return keys -} - -func (c *OnlineMap) RemoveExpiredIPs(list map[string]time.Time) map[string]time.Time { - c.access.Lock() - defer c.access.Unlock() - - now := time.Now() - for k, t := range list { - diff := now.Sub(t) - if diff.Seconds() > 20 { - delete(list, k) - } - } - return list -} - -func (c *OnlineMap) IpTimeMap() map[string]time.Time { - list := c.ipList - if time.Since(c.lastCleanup) > c.cleanupPeriod { - list = c.RemoveExpiredIPs(list) - c.lastCleanup = time.Now() - } - - return c.ipList -} diff --git a/app/stats/stats.go b/app/stats/stats.go index 5451c5e6..c82277c3 100644 --- a/app/stats/stats.go +++ b/app/stats/stats.go @@ -1,5 +1,7 @@ package stats +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "sync" @@ -11,19 +13,17 @@ import ( // Manager is an implementation of stats.Manager. type Manager struct { - access sync.RWMutex - counters map[string]*Counter - onlineMap map[string]*OnlineMap - channels map[string]*Channel - running bool + access sync.RWMutex + counters map[string]*Counter + channels map[string]*Channel + running bool } // NewManager creates an instance of Statistics Manager. func NewManager(ctx context.Context, config *Config) (*Manager, error) { m := &Manager{ - counters: make(map[string]*Counter), - onlineMap: make(map[string]*OnlineMap), - channels: make(map[string]*Channel), + counters: make(map[string]*Counter), + channels: make(map[string]*Channel), } return m, nil @@ -40,9 +40,9 @@ func (m *Manager) RegisterCounter(name string) (stats.Counter, error) { defer m.access.Unlock() if _, found := m.counters[name]; found { - return nil, errors.New("Counter ", name, " already registered.") + return nil, newError("Counter ", name, " already registered.") } - errors.LogDebug(context.Background(), "create new counter ", name) + newError("create new counter ", name).AtDebug().WriteToLog() c := new(Counter) m.counters[name] = c return c, nil @@ -54,7 +54,7 @@ func (m *Manager) UnregisterCounter(name string) error { defer m.access.Unlock() if _, found := m.counters[name]; found { - errors.LogDebug(context.Background(), "remove counter ", name) + newError("remove counter ", name).AtDebug().WriteToLog() delete(m.counters, name) } return nil @@ -83,52 +83,15 @@ func (m *Manager) VisitCounters(visitor func(string, stats.Counter) bool) { } } -// RegisterOnlineMap implements stats.Manager. -func (m *Manager) RegisterOnlineMap(name string) (stats.OnlineMap, error) { - m.access.Lock() - defer m.access.Unlock() - - if _, found := m.onlineMap[name]; found { - return nil, errors.New("onlineMap ", name, " already registered.") - } - errors.LogDebug(context.Background(), "create new onlineMap ", name) - om := NewOnlineMap() - m.onlineMap[name] = om - return om, nil -} - -// UnregisterOnlineMap implements stats.Manager. -func (m *Manager) UnregisterOnlineMap(name string) error { - m.access.Lock() - defer m.access.Unlock() - - if _, found := m.onlineMap[name]; found { - errors.LogDebug(context.Background(), "remove onlineMap ", name) - delete(m.onlineMap, name) - } - return nil -} - -// GetOnlineMap implements stats.Manager. -func (m *Manager) GetOnlineMap(name string) stats.OnlineMap { - m.access.RLock() - defer m.access.RUnlock() - - if om, found := m.onlineMap[name]; found { - return om - } - return nil -} - // RegisterChannel implements stats.Manager. func (m *Manager) RegisterChannel(name string) (stats.Channel, error) { m.access.Lock() defer m.access.Unlock() if _, found := m.channels[name]; found { - return nil, errors.New("Channel ", name, " already registered.") + return nil, newError("Channel ", name, " already registered.") } - errors.LogDebug(context.Background(), "create new channel ", name) + newError("create new channel ", name).AtDebug().WriteToLog() c := NewChannel(&ChannelConfig{BufferSize: 64, Blocking: false}) m.channels[name] = c if m.running { @@ -143,7 +106,7 @@ func (m *Manager) UnregisterChannel(name string) error { defer m.access.Unlock() if c, found := m.channels[name]; found { - errors.LogDebug(context.Background(), "remove channel ", name) + newError("remove channel ", name).AtDebug().WriteToLog() delete(m.channels, name) return c.Close() } @@ -185,7 +148,7 @@ func (m *Manager) Close() error { m.running = false errs := []error{} for name, channel := range m.channels { - errors.LogDebug(context.Background(), "remove channel ", name) + newError("remove channel ", name).AtDebug().WriteToLog() delete(m.channels, name) if err := channel.Close(); err != nil { errs = append(errs, err) diff --git a/common/buf/buf.go b/common/buf/buf.go index 9f5c5cbe..dde7e22c 100644 --- a/common/buf/buf.go +++ b/common/buf/buf.go @@ -1,2 +1,4 @@ // Package buf provides a light-weight memory allocation mechanism. package buf // import "github.com/xtls/xray-core/common/buf" + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 2aa60e6a..82795b98 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -4,7 +4,6 @@ import ( "io" "github.com/xtls/xray-core/common/bytespool" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" ) @@ -13,19 +12,8 @@ const ( Size = 8192 ) -var zero = [Size * 10]byte{0} - var pool = bytespool.GetPool(Size) -// ownership represents the data owner of the buffer. -type ownership uint8 - -const ( - managed ownership = iota - unmanaged - bytespools -) - // Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles // the buffer into an internal buffer pool, in order to recreate a buffer more // quickly. @@ -33,11 +21,11 @@ type Buffer struct { v []byte start int32 end int32 - ownership ownership + unmanaged bool UDP *net.Destination } -// New creates a Buffer with 0 length and 8K capacity, managed. +// New creates a Buffer with 0 length and 8K capacity. func New() *Buffer { buf := pool.Get().([]byte) if cap(buf) >= Size { @@ -51,7 +39,7 @@ func New() *Buffer { } } -// NewExisted creates a standard size Buffer with an existed bytearray, managed. +// NewExisted creates a managed, standard size Buffer with an existed bytearray func NewExisted(b []byte) *Buffer { if cap(b) < Size { panic("Invalid buffer") @@ -68,16 +56,16 @@ func NewExisted(b []byte) *Buffer { } } -// FromBytes creates a Buffer with an existed bytearray, unmanaged. +// FromBytes creates a Buffer with an existed bytearray func FromBytes(b []byte) *Buffer { return &Buffer{ v: b, end: int32(len(b)), - ownership: unmanaged, + unmanaged: true, } } -// StackNew creates a new Buffer object on stack, managed. +// StackNew creates a new Buffer object on stack. // This method is for buffers that is released in the same function. func StackNew() Buffer { buf := pool.Get().([]byte) @@ -92,17 +80,9 @@ func StackNew() Buffer { } } -// NewWithSize creates a Buffer with 0 length and capacity with at least the given size, bytespool's. -func NewWithSize(size int32) *Buffer { - return &Buffer{ - v: bytespool.Alloc(size), - ownership: bytespools, - } -} - // Release recycles the buffer into an internal buffer pool. func (b *Buffer) Release() { - if b == nil || b.v == nil || b.ownership == unmanaged { + if b == nil || b.v == nil || b.unmanaged { return } @@ -110,13 +90,8 @@ func (b *Buffer) Release() { b.v = nil b.Clear() - switch b.ownership { - case managed: - if cap(p) == Size { - pool.Put(p) - } - case bytespools: - bytespool.Free(p) + if cap(p) == Size { + pool.Put(p) } b.UDP = nil } @@ -152,7 +127,6 @@ func (b *Buffer) Extend(n int32) []byte { } ext := b.v[b.end:end] b.end = end - copy(ext, zero[:]) return ext } @@ -201,7 +175,6 @@ func (b *Buffer) Check() { // Resize cuts the buffer at the given position. func (b *Buffer) Resize(from, to int32) { - oldEnd := b.end if from < 0 { from += b.Len() } @@ -214,9 +187,6 @@ func (b *Buffer) Resize(from, to int32) { b.end = b.start + to b.start += from b.Check() - if b.end > oldEnd { - copy(b.v[oldEnd:b.end], zero[:]) - } } // Advance cuts the buffer at the given position. @@ -236,14 +206,6 @@ func (b *Buffer) Len() int32 { return b.end - b.start } -// Cap returns the capacity of the buffer content. -func (b *Buffer) Cap() int32 { - if b == nil { - return 0 - } - return int32(len(b.v)) -} - // IsEmpty returns true if the buffer is empty. func (b *Buffer) IsEmpty() bool { return b.Len() == 0 @@ -264,7 +226,7 @@ func (b *Buffer) Write(data []byte) (int, error) { // WriteByte writes a single byte into the buffer. func (b *Buffer) WriteByte(v byte) error { if b.IsFull() { - return errors.New("buffer full") + return newError("buffer full") } b.v[b.end] = v b.end++ @@ -324,7 +286,7 @@ func (b *Buffer) ReadFullFrom(reader io.Reader, size int32) (int64, error) { end := b.end + size if end > int32(len(b.v)) { v := end - return 0, errors.New("out of bound: ", v) + return 0, newError("out of bound: ", v) } n, err := io.ReadFull(reader, b.v[b.end:end]) b.end += int32(n) diff --git a/common/buf/copy.go b/common/buf/copy.go index 4cc3be88..601771be 100644 --- a/common/buf/copy.go +++ b/common/buf/copy.go @@ -6,7 +6,6 @@ import ( "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/signal" - "github.com/xtls/xray-core/features/stats" ) type dataHandler func(MultiBuffer) @@ -41,17 +40,6 @@ func CountSize(sc *SizeCounter) CopyOption { } } -// AddToStatCounter a CopyOption add to stat counter -func AddToStatCounter(sc stats.Counter) CopyOption { - return func(handler *copyHandler) { - handler.onData = append(handler.onData, func(b MultiBuffer) { - if sc != nil { - sc.Add(int64(b.Len())) - } - }) - } -} - type readError struct { error } @@ -120,7 +108,7 @@ func Copy(reader Reader, writer Writer, options ...CopyOption) error { return nil } -var ErrNotTimeoutReader = errors.New("not a TimeoutReader") +var ErrNotTimeoutReader = newError("not a TimeoutReader") func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error { timeoutReader, ok := reader.(TimeoutReader) diff --git a/common/buf/copy_test.go b/common/buf/copy_test.go index 3c896801..cb2f831b 100644 --- a/common/buf/copy_test.go +++ b/common/buf/copy_test.go @@ -27,7 +27,7 @@ func TestReadError(t *testing.T) { t.Error("expected to be ReadError, but not") } - if err.Error() != "common/buf_test: error" { + if err.Error() != "error" { t.Fatal("unexpected error message: ", err.Error()) } } @@ -48,7 +48,7 @@ func TestWriteError(t *testing.T) { t.Error("expected to be WriteError, but not") } - if err.Error() != "common/buf_test: error" { + if err.Error() != "error" { t.Fatal("unexpected error message: ", err.Error()) } } diff --git a/common/buf/errors.generated.go b/common/buf/errors.generated.go new file mode 100644 index 00000000..032c0701 --- /dev/null +++ b/common/buf/errors.generated.go @@ -0,0 +1,9 @@ +package buf + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/buf/io.go b/common/buf/io.go index 0974b4f3..f0b7689c 100644 --- a/common/buf/io.go +++ b/common/buf/io.go @@ -1,14 +1,12 @@ package buf import ( - "context" "io" "net" "os" "syscall" "time" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/transport/internet/stat" ) @@ -20,7 +18,7 @@ type Reader interface { } // ErrReadTimeout is an error that happens with IO timeout. -var ErrReadTimeout = errors.New("IO timeout") +var ErrReadTimeout = newError("IO timeout") // TimeoutReader is a reader that returns error if Read() operation takes longer than the given timeout. type TimeoutReader interface { @@ -76,7 +74,7 @@ func NewReader(reader io.Reader) Reader { if sc, ok := reader.(syscall.Conn); ok { rawConn, err := sc.SyscallConn() if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to get sysconn") + newError("failed to get sysconn").Base(err).WriteToLog() } else { var counter stats.Counter diff --git a/common/buf/multi_buffer_test.go b/common/buf/multi_buffer_test.go index 2b83a548..4f9499af 100644 --- a/common/buf/multi_buffer_test.go +++ b/common/buf/multi_buffer_test.go @@ -106,7 +106,7 @@ func TestMultiBufferReadAllToByte(t *testing.T) { common.Must(err) if l := len(b); l != 8*1024 { - t.Error("unexpected length from ReadAllToBytes", l) + t.Error("unexpceted length from ReadAllToBytes", l) } } { @@ -139,7 +139,7 @@ func TestMultiBufferCopy(t *testing.T) { mb.Copy(lbdst) if d := cmp.Diff(lb, lbdst); d != "" { - t.Error("unexpected different from MultiBufferCopy ", d) + t.Error("unexpceted different from MultiBufferCopy ", d) } } diff --git a/common/buf/override.go b/common/buf/override.go deleted file mode 100644 index 7b2f1554..00000000 --- a/common/buf/override.go +++ /dev/null @@ -1,38 +0,0 @@ -package buf - -import ( - "github.com/xtls/xray-core/common/net" -) - -type EndpointOverrideReader struct { - Reader - Dest net.Address - OriginalDest net.Address -} - -func (r *EndpointOverrideReader) ReadMultiBuffer() (MultiBuffer, error) { - mb, err := r.Reader.ReadMultiBuffer() - if err == nil { - for _, b := range mb { - if b.UDP != nil && b.UDP.Address == r.OriginalDest { - b.UDP.Address = r.Dest - } - } - } - return mb, err -} - -type EndpointOverrideWriter struct { - Writer - Dest net.Address - OriginalDest net.Address -} - -func (w *EndpointOverrideWriter) WriteMultiBuffer(mb MultiBuffer) error { - for _, b := range mb { - if b.UDP != nil && b.UDP.Address == w.Dest { - b.UDP.Address = w.OriginalDest - } - } - return w.Writer.WriteMultiBuffer(mb) -} diff --git a/common/buf/reader.go b/common/buf/reader.go index 33d362d4..3e35ad62 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -21,7 +21,7 @@ func readOneUDP(r io.Reader) (*Buffer, error) { } b.Release() - return nil, errors.New("Reader returns too many empty payloads.") + return nil, newError("Reader returns too many empty payloads.") } // ReadBuffer reads a Buffer from the given reader. @@ -41,8 +41,8 @@ type BufferedReader struct { Reader Reader // Buffer is the internal buffer to be read from first Buffer MultiBuffer - // Splitter is a function to read bytes from MultiBuffer - Splitter func(MultiBuffer, []byte) (MultiBuffer, int) + // Spliter is a function to read bytes from MultiBuffer + Spliter func(MultiBuffer, []byte) (MultiBuffer, int) } // BufferedBytes returns the number of bytes that is cached in this reader. @@ -59,7 +59,7 @@ func (r *BufferedReader) ReadByte() (byte, error) { // Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader. func (r *BufferedReader) Read(b []byte) (int, error) { - spliter := r.Splitter + spliter := r.Spliter if spliter == nil { spliter = SplitBytes } diff --git a/common/buf/readv_reader.go b/common/buf/readv_reader.go index bcd0f0ed..f897ccca 100644 --- a/common/buf/readv_reader.go +++ b/common/buf/readv_reader.go @@ -147,7 +147,7 @@ var useReadv bool func init() { const defaultFlagValue = "NOT_DEFINED_AT_ALL" - value := platform.NewEnvFlag(platform.UseReadV).GetValue(func() string { return defaultFlagValue }) + value := platform.NewEnvFlag("xray.buf.readv").GetValue(func() string { return defaultFlagValue }) switch value { case defaultFlagValue, "auto", "enable": useReadv = true diff --git a/common/common.go b/common/common.go index a09f6fbe..cada9973 100644 --- a/common/common.go +++ b/common/common.go @@ -12,6 +12,8 @@ import ( "github.com/xtls/xray-core/common/errors" ) +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + // ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route. var ErrNoClue = errors.New("not enough information for making a decision") @@ -38,7 +40,7 @@ func Error2(v interface{}, err error) error { func envFile() (string, error) { if file := os.Getenv("GOENV"); file != "" { if file == "off" { - return "", errors.New("GOENV=off") + return "", fmt.Errorf("GOENV=off") } return file, nil } @@ -47,7 +49,7 @@ func envFile() (string, error) { return "", err } if dir == "" { - return "", errors.New("missing user-config dir") + return "", fmt.Errorf("missing user-config dir") } return filepath.Join(dir, "go", "env"), nil } @@ -60,7 +62,7 @@ func GetRuntimeEnv(key string) (string, error) { return "", err } if file == "" { - return "", errors.New("missing runtime env file") + return "", fmt.Errorf("missing runtime env file") } var data []byte var runtimeEnv string diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 6259e2a7..88de851e 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -8,7 +8,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/bytespool" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" ) @@ -64,7 +63,7 @@ type AEADAuthenticator struct { func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) { iv := v.NonceGenerator() if len(iv) != v.AEAD.NonceSize() { - return nil, errors.New("invalid AEAD nonce size: ", len(iv)) + return nil, newError("invalid AEAD nonce size: ", len(iv)) } var additionalData []byte @@ -77,7 +76,7 @@ func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) { func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) { iv := v.NonceGenerator() if len(iv) != v.AEAD.NonceSize() { - return nil, errors.New("invalid AEAD nonce size: ", len(iv)) + return nil, newError("invalid AEAD nonce size: ", len(iv)) } var additionalData []byte @@ -132,7 +131,7 @@ func (r *AuthenticationReader) readSize() (uint16, uint16, error) { return size, padding, err } -var errSoft = errors.New("waiting for more data") +var errSoft = newError("waiting for more data") func (r *AuthenticationReader) readBuffer(size int32, padding int32) (*buf.Buffer, error) { b := buf.New() @@ -179,7 +178,7 @@ func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) erro if size <= buf.Size { b, err := r.readBuffer(int32(size), int32(padding)) if err != nil { - return err + return nil } *mb = append(*mb, b) return nil @@ -256,7 +255,7 @@ func (w *AuthenticationWriter) seal(b []byte) (*buf.Buffer, error) { sizeBytes := w.sizeParser.SizeBytes() totalSize := sizeBytes + encryptedSize + paddingSize if totalSize > buf.Size { - return nil, errors.New("size too large: ", totalSize) + return nil, newError("size too large: ", totalSize) } eb := buf.New() diff --git a/common/crypto/crypto.go b/common/crypto/crypto.go index 464b4fa7..a7ed1a4a 100644 --- a/common/crypto/crypto.go +++ b/common/crypto/crypto.go @@ -1,15 +1,4 @@ // Package crypto provides common crypto libraries for Xray. package crypto // import "github.com/xtls/xray-core/common/crypto" -import ( - "crypto/rand" - "math/big" -) - -func RandBetween(from int64, to int64) int64 { - if from == to { - return from - } - bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from)) - return from + bigInt.Int64() -} +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/common/crypto/errors.generated.go b/common/crypto/errors.generated.go new file mode 100644 index 00000000..2d983f89 --- /dev/null +++ b/common/crypto/errors.generated.go @@ -0,0 +1,9 @@ +package crypto + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/ctx/context.go b/common/ctx/context.go deleted file mode 100644 index 25144f5f..00000000 --- a/common/ctx/context.go +++ /dev/null @@ -1,25 +0,0 @@ -package ctx - -import "context" - -type SessionKey int - -// ID of a session. -type ID uint32 - -const ( - idSessionKey SessionKey = 0 -) - -// ContextWithID returns a new context with the given ID. -func ContextWithID(ctx context.Context, id ID) context.Context { - return context.WithValue(ctx, idSessionKey, id) -} - -// IDFromContext returns ID in this context, or 0 if not contained. -func IDFromContext(ctx context.Context) ID { - if id, ok := ctx.Value(idSessionKey).(ID); ok { - return id - } - return 0 -} diff --git a/common/dice/dice.go b/common/dice/dice.go index 0a0a40e4..896c1e8e 100644 --- a/common/dice/dice.go +++ b/common/dice/dice.go @@ -4,6 +4,7 @@ package dice // import "github.com/xtls/xray-core/common/dice" import ( "math/rand" + "time" ) // Roll returns a non-negative number between 0 (inclusive) and n (exclusive). @@ -14,14 +15,6 @@ func Roll(n int) int { return rand.Intn(n) } -// RollInt63n returns a non-negative number between 0 (inclusive) and n (exclusive). -func RollInt63n(n int64) int64 { - if n == 1 { - return 0 - } - return rand.Int63n(n) -} - // Roll returns a non-negative number between 0 (inclusive) and n (exclusive). func RollDeterministic(n int, seed int64) int { if n == 1 { @@ -53,3 +46,7 @@ func (dd *DeterministicDice) Roll(n int) int { } return dd.Intn(n) } + +func init() { + rand.Seed(time.Now().Unix()) +} diff --git a/common/drain/drain.go b/common/drain/drain.go index 5a3be246..5a935789 100644 --- a/common/drain/drain.go +++ b/common/drain/drain.go @@ -2,6 +2,8 @@ package drain import "io" +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + type Drainer interface { AcknowledgeReceive(size int) Drain(reader io.Reader) error diff --git a/common/drain/drainer.go b/common/drain/drainer.go index 16ed1f23..bdb12d4f 100644 --- a/common/drain/drainer.go +++ b/common/drain/drainer.go @@ -2,9 +2,9 @@ package drain import ( "io" + "io/ioutil" "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" ) type BehaviorSeedLimitedDrainer struct { @@ -28,15 +28,15 @@ func (d *BehaviorSeedLimitedDrainer) Drain(reader io.Reader) error { if d.DrainSize > 0 { err := drainReadN(reader, d.DrainSize) if err == nil { - return errors.New("drained connection") + return newError("drained connection") } - return errors.New("unable to drain connection").Base(err) + return newError("unable to drain connection").Base(err) } return nil } func drainReadN(reader io.Reader, n int) error { - _, err := io.CopyN(io.Discard, reader, int64(n)) + _, err := io.CopyN(ioutil.Discard, reader, int64(n)) return err } @@ -45,7 +45,7 @@ func WithError(drainer Drainer, reader io.Reader, err error) error { if drainErr == nil { return err } - return errors.New(drainErr).Base(err) + return newError(drainErr).Base(err) } type NopDrainer struct{} diff --git a/common/drain/errors.generated.go b/common/drain/errors.generated.go new file mode 100644 index 00000000..1535361e --- /dev/null +++ b/common/drain/errors.generated.go @@ -0,0 +1,9 @@ +package drain + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/errors.generated.go b/common/errors.generated.go new file mode 100644 index 00000000..28b9a625 --- /dev/null +++ b/common/errors.generated.go @@ -0,0 +1,9 @@ +package common + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/errors/errorgen/main.go b/common/errors/errorgen/main.go new file mode 100644 index 00000000..4532fd8d --- /dev/null +++ b/common/errors/errorgen/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" +) + +func main() { + pwd, err := os.Getwd() + if err != nil { + fmt.Println("can not get current working directory") + os.Exit(1) + } + pkg := filepath.Base(pwd) + if pkg == "xray-core" { + pkg = "core" + } + + file, err := os.OpenFile("errors.generated.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o644) + if err != nil { + fmt.Printf("Failed to generate errors.generated.go: %v", err) + os.Exit(1) + } + defer file.Close() + + fmt.Fprintf(file, `package %s + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} +`, pkg) +} diff --git a/common/errors/errors.go b/common/errors/errors.go index 7a35f254..ef1dd6f9 100644 --- a/common/errors/errors.go +++ b/common/errors/errors.go @@ -2,11 +2,9 @@ package errors // import "github.com/xtls/xray-core/common/errors" import ( - "context" - "runtime" + "reflect" "strings" - c "github.com/xtls/xray-core/common/ctx" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/serial" ) @@ -24,13 +22,29 @@ type hasSeverity interface { // Error is an error object with underlying error. type Error struct { + pathObj interface{} prefix []interface{} message []interface{} - caller string inner error severity log.Severity } +func (err *Error) WithPathObj(obj interface{}) *Error { + err.pathObj = obj + return err +} + +func (err *Error) pkgPath() string { + if err.pathObj == nil { + return "" + } + path := reflect.TypeOf(err.pathObj).PkgPath() + if len(path) >= trim { + return path[trim:] + } + return path +} + // Error implements error.Error(). func (err *Error) Error() string { builder := strings.Builder{} @@ -40,8 +54,9 @@ func (err *Error) Error() string { builder.WriteString("] ") } - if len(err.caller) > 0 { - builder.WriteString(err.caller) + path := err.pkgPath() + if len(path) > 0 { + builder.WriteString(path) builder.WriteString(": ") } @@ -114,6 +129,24 @@ func (err *Error) String() string { return err.Error() } +// WriteToLog writes current error into log. +func (err *Error) WriteToLog(opts ...ExportOption) { + var holder ExportOptionHolder + + for _, opt := range opts { + opt(&holder) + } + + if holder.SessionID > 0 { + err.prefix = append(err.prefix, holder.SessionID) + } + + log.Record(&log.GeneralMessage{ + Severity: GetSeverity(err), + Content: err, + }) +} + type ExportOptionHolder struct { SessionID uint32 } @@ -122,82 +155,12 @@ type ExportOption func(*ExportOptionHolder) // New returns a new error object with message formed from given arguments. func New(msg ...interface{}) *Error { - pc, _, _, _ := runtime.Caller(1) - details := runtime.FuncForPC(pc).Name() - if len(details) >= trim { - details = details[trim:] - } - i := strings.Index(details, ".") - if i > 0 { - details = details[:i] - } return &Error{ message: msg, severity: log.Severity_Info, - caller: details, } } -func LogDebug(ctx context.Context, msg ...interface{}) { - doLog(ctx, nil, log.Severity_Debug, msg...) -} - -func LogDebugInner(ctx context.Context, inner error, msg ...interface{}) { - doLog(ctx, inner, log.Severity_Debug, msg...) -} - -func LogInfo(ctx context.Context, msg ...interface{}) { - doLog(ctx, nil, log.Severity_Info, msg...) -} - -func LogInfoInner(ctx context.Context, inner error, msg ...interface{}) { - doLog(ctx, inner, log.Severity_Info, msg...) -} - -func LogWarning(ctx context.Context, msg ...interface{}) { - doLog(ctx, nil, log.Severity_Warning, msg...) -} - -func LogWarningInner(ctx context.Context, inner error, msg ...interface{}) { - doLog(ctx, inner, log.Severity_Warning, msg...) -} - -func LogError(ctx context.Context, msg ...interface{}) { - doLog(ctx, nil, log.Severity_Error, msg...) -} - -func LogErrorInner(ctx context.Context, inner error, msg ...interface{}) { - doLog(ctx, inner, log.Severity_Error, msg...) -} - -func doLog(ctx context.Context, inner error, severity log.Severity, msg ...interface{}) { - pc, _, _, _ := runtime.Caller(2) - details := runtime.FuncForPC(pc).Name() - if len(details) >= trim { - details = details[trim:] - } - i := strings.Index(details, ".") - if i > 0 { - details = details[:i] - } - err := &Error{ - message: msg, - severity: severity, - caller: details, - inner: inner, - } - if ctx != nil && ctx != context.Background() { - id := uint32(c.IDFromContext(ctx)) - if id > 0 { - err.prefix = append(err.prefix, id) - } - } - log.Record(&log.GeneralMessage{ - Severity: GetSeverity(err), - Content: err, - }) -} - // Cause returns the root cause of this error. func Cause(err error) error { if err == nil { diff --git a/common/errors/errors_test.go b/common/errors/errors_test.go index 3a1cb134..a38e8733 100644 --- a/common/errors/errors_test.go +++ b/common/errors/errors_test.go @@ -36,14 +36,20 @@ func TestError(t *testing.T) { } } +type e struct{} + func TestErrorMessage(t *testing.T) { data := []struct { err error msg string }{ { - err: New("a").Base(New("b")), - msg: "common/errors_test: a > common/errors_test: b", + err: New("a").Base(New("b")).WithPathObj(e{}), + msg: "common/errors_test: a > b", + }, + { + err: New("a").Base(New("b").WithPathObj(e{})), + msg: "a > common/errors_test: b", }, } diff --git a/common/errors/feature_errors.go b/common/errors/feature_errors.go deleted file mode 100644 index 40c0ee9b..00000000 --- a/common/errors/feature_errors.go +++ /dev/null @@ -1,34 +0,0 @@ -package errors - -import ( - "context" -) - -// PrintMigrateFeatureInfo prints a notice of the upcoming feature migration. -// Place it after the source feature related config file pharser code. -// Important note: Only use this when the target migrating feature is under construction. -// Important note: Even when the target migrating feature has finished its construction, this notice can still be used yet before announcing deprecation of the old feature. -// Do not remove this function even there is no reference to it. -func PrintMigrateFeatureInfo(sourceFeature string, targetFeature string) { - LogInfo(context.Background(), "The feature "+sourceFeature+" will be migrated to "+targetFeature+" in the future.") -} - -// PrintDeprecatedFeatureWarning prints a warning for deprecated and going to be removed feature. -// Do not remove this function even there is no reference to it. -func PrintDeprecatedFeatureWarning(feature string, migrateFeature string) { - if len(migrateFeature) > 0 { - LogWarning(context.Background(), "This feature "+feature+" is deprecated and being migrated to "+migrateFeature+". Please update your config(s) according to release note and documentation before removal.") - } else { - LogWarning(context.Background(), "This feature "+feature+" is deprecated. Please update your config(s) according to release note and documentation before removal.") - } -} - -// PrintRemovedFeatureError prints an error message for removed feature then return an error. And after long enough time the message can also be removed, uses as an indicator. -// Do not remove this function even there is no reference to it. -func PrintRemovedFeatureError(feature string, migrateFeature string) error { - if len(migrateFeature) > 0 { - return New("The feature " + feature + " has been removed and migrated to " + migrateFeature + ". Please update your config(s) according to release note and documentation.") - } else { - return New("The feature " + feature + " has been removed. Please update your config(s) according to release note and documentation.") - } -} diff --git a/common/errors/multi_error.go b/common/errors/multi_error.go index cdfec9cd..8f19c97a 100644 --- a/common/errors/multi_error.go +++ b/common/errors/multi_error.go @@ -1,7 +1,6 @@ package errors import ( - "errors" "strings" ) @@ -29,20 +28,3 @@ func Combine(maybeError ...error) error { } return errs } - -func AllEqual(expected error, actual error) bool { - switch errs := actual.(type) { - case multiError: - if len(errs) == 0 { - return false - } - for _, err := range errs { - if !errors.Is(err, expected) { - return false - } - } - return true - default: - return errors.Is(errs, expected) - } -} diff --git a/common/log/access.go b/common/log/access.go index 204212dc..22b84cf6 100644 --- a/common/log/access.go +++ b/common/log/access.go @@ -31,8 +31,6 @@ type AccessMessage struct { func (m *AccessMessage) String() string { builder := strings.Builder{} - builder.WriteString("from") - builder.WriteByte(' ') builder.WriteString(serial.ToString(m.From)) builder.WriteByte(' ') builder.WriteString(string(m.Status)) diff --git a/common/log/log.pb.go b/common/log/log.pb.go index d0e1d17a..321ae16b 100644 --- a/common/log/log.pb.go +++ b/common/log/log.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/log/log.proto package log @@ -106,7 +106,7 @@ func file_common_log_log_proto_rawDescGZIP() []byte { } var file_common_log_log_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_common_log_log_proto_goTypes = []any{ +var file_common_log_log_proto_goTypes = []interface{}{ (Severity)(0), // 0: xray.common.log.Severity } var file_common_log_log_proto_depIdxs = []int32{ diff --git a/common/log/logger.go b/common/log/logger.go index 7c100aa1..79507964 100644 --- a/common/log/logger.go +++ b/common/log/logger.go @@ -27,11 +27,6 @@ type generalLogger struct { done *done.Instance } -type serverityLogger struct { - inner *generalLogger - logLevel Severity -} - // NewLogger returns a generic log handler that can handle all type of messages. func NewLogger(logWriterCreator WriterCreator) Handler { return &generalLogger{ @@ -42,32 +37,6 @@ func NewLogger(logWriterCreator WriterCreator) Handler { } } -func ReplaceWithSeverityLogger(serverity Severity) { - w := CreateStdoutLogWriter() - g := &generalLogger{ - creator: w, - buffer: make(chan Message, 16), - access: semaphore.New(1), - done: done.New(), - } - s := &serverityLogger{ - inner: g, - logLevel: serverity, - } - RegisterHandler(s) -} - -func (l *serverityLogger) Handle(msg Message) { - switch msg := msg.(type) { - case *GeneralMessage: - if msg.Severity <= l.logLevel { - l.inner.Handle(msg) - } - default: - l.inner.Handle(msg) - } -} - func (l *generalLogger) run() { defer l.access.Signal() @@ -98,7 +67,6 @@ func (l *generalLogger) run() { } func (l *generalLogger) Handle(msg Message) { - select { case l.buffer <- msg: default: @@ -146,7 +114,7 @@ func (w *fileLogWriter) Close() error { func CreateStdoutLogWriter() WriterCreator { return func() Writer { return &consoleLogWriter{ - logger: log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lmicroseconds), + logger: log.New(os.Stdout, "", log.Ldate|log.Ltime), } } } @@ -155,7 +123,7 @@ func CreateStdoutLogWriter() WriterCreator { func CreateStderrLogWriter() WriterCreator { return func() Writer { return &consoleLogWriter{ - logger: log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lmicroseconds), + logger: log.New(os.Stderr, "", log.Ldate|log.Ltime), } } } @@ -174,7 +142,7 @@ func CreateFileLogWriter(path string) (WriterCreator, error) { } return &fileLogWriter{ file: file, - logger: log.New(file, "", log.Ldate|log.Ltime|log.Lmicroseconds), + logger: log.New(file, "", log.Ldate|log.Ltime), } }, nil } diff --git a/common/log/logger_test.go b/common/log/logger_test.go index 6a664e08..1c7f06e2 100644 --- a/common/log/logger_test.go +++ b/common/log/logger_test.go @@ -16,7 +16,6 @@ func TestFileLogger(t *testing.T) { common.Must(err) path := f.Name() common.Must(f.Close()) - defer os.Remove(path) creator, err := CreateFileLogWriter(path) common.Must(err) diff --git a/common/mux/client.go b/common/mux/client.go index df74ef17..2019738f 100644 --- a/common/mux/client.go +++ b/common/mux/client.go @@ -14,7 +14,6 @@ import ( "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/task" - "github.com/xtls/xray-core/common/xudp" "github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" @@ -22,7 +21,7 @@ import ( ) type ClientManager struct { - Enabled bool // whether mux is enabled from user config + Enabled bool // wheather mux is enabled from user config Picker WorkerPicker } @@ -37,7 +36,7 @@ func (m *ClientManager) Dispatch(ctx context.Context, link *transport.Link) erro } } - return errors.New("unable to find an available mux client").AtWarning() + return newError("unable to find an available mux client").AtWarning() } type WorkerPicker interface { @@ -57,7 +56,7 @@ func (p *IncrementalWorkerPicker) cleanupFunc() error { defer p.access.Unlock() if len(p.workers) == 0 { - return errors.New("no worker") + return newError("no worker") } p.cleanup() @@ -148,14 +147,13 @@ func (f *DialingWorkerFactory) Create() (*ClientWorker, error) { } go func(p proxy.Outbound, d internet.Dialer, c common.Closable) { - outbounds := []*session.Outbound{{ + ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{ Target: net.TCPDestination(muxCoolAddress, muxCoolPort), - }} - ctx := session.ContextWithOutbounds(context.Background(), outbounds) + }) ctx, cancel := context.WithCancel(ctx) if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil { - errors.LogInfoInner(ctx, err, "failed to handler mux client connection") + errors.New("failed to handler mux client connection").Base(err).WriteToLog() } common.Must(c.Close()) cancel() @@ -243,27 +241,28 @@ func writeFirstPayload(reader buf.Reader, writer *Writer) error { } func fetchInput(ctx context.Context, s *Session, output buf.Writer) { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] + dest := session.OutboundFromContext(ctx).Target transferType := protocol.TransferTypeStream - if ob.Target.Network == net.Network_UDP { + if dest.Network == net.Network_UDP { transferType = protocol.TransferTypePacket } s.transferType = transferType - writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx)) - defer s.Close(false) + writer := NewWriter(s.ID, dest, output, transferType) + defer s.Close() defer writer.Close() - errors.LogInfo(ctx, "dispatching request to ", ob.Target) + newError("dispatching request to ", dest).WriteToLog(session.ExportIDToError(ctx)) if err := writeFirstPayload(s.input, writer); err != nil { - errors.LogInfoInner(ctx, err, "failed to write first payload") + newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx)) writer.hasError = true + common.Interrupt(s.input) return } if err := buf.Copy(s.input, writer); err != nil { - errors.LogInfoInner(ctx, err, "failed to fetch all input") + newError("failed to fetch all input").Base(err).WriteToLog(session.ExportIDToError(ctx)) writer.hasError = true + common.Interrupt(s.input) return } } @@ -335,9 +334,16 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere rr := s.NewReader(reader, &meta.Target) err := buf.Copy(rr, s.output) if err != nil && buf.IsWriteError(err) { - errors.LogInfoInner(context.Background(), err, "failed to write to downstream. closing session ", s.ID) - s.Close(false) - return buf.Copy(rr, buf.Discard) + newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog() + + // Notify remote peer to close this session. + closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream) + closingWriter.Close() + + drainErr := buf.Copy(rr, buf.Discard) + common.Interrupt(s.input) + s.Close() + return drainErr } return err @@ -345,7 +351,12 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { if s, found := m.sessionManager.Get(meta.SessionID); found { - s.Close(false) + if meta.Option.Has(OptionError) { + common.Interrupt(s.input) + common.Interrupt(s.output) + } + common.Interrupt(s.input) + s.Close() } if meta.Option.Has(OptionData) { return buf.Copy(NewStreamReader(reader), buf.Discard) @@ -365,7 +376,7 @@ func (m *ClientWorker) fetchOutput() { err := meta.Unmarshal(reader) if err != nil { if errors.Cause(err) != io.EOF { - errors.LogInfoInner(context.Background(), err, "failed to read metadata") + newError("failed to read metadata").Base(err).WriteToLog() } break } @@ -381,12 +392,12 @@ func (m *ClientWorker) fetchOutput() { err = m.handleStatusKeep(&meta, reader) default: status := meta.SessionStatus - errors.LogError(context.Background(), "unknown status: ", status) + newError("unknown status: ", status).AtError().WriteToLog() return } if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to process data") + newError("failed to process data").Base(err).WriteToLog() return } } diff --git a/common/mux/client_test.go b/common/mux/client_test.go index 9626e2a2..7837a86e 100644 --- a/common/mux/client_test.go +++ b/common/mux/client_test.go @@ -86,9 +86,9 @@ func TestClientWorkerClose(t *testing.T) { } tr1, tw1 := pipe.New(pipe.WithoutSizeLimit()) - ctx1 := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ + ctx1 := session.ContextWithOutbound(context.Background(), &session.Outbound{ Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80), - }}) + }) common.Must(manager.Dispatch(ctx1, &transport.Link{ Reader: tr1, Writer: tw1, @@ -103,9 +103,9 @@ func TestClientWorkerClose(t *testing.T) { } tr2, tw2 := pipe.New(pipe.WithoutSizeLimit()) - ctx2 := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ + ctx2 := session.ContextWithOutbound(context.Background(), &session.Outbound{ Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80), - }}) + }) common.Must(manager.Dispatch(ctx2, &transport.Link{ Reader: tr2, Writer: tw2, diff --git a/common/mux/errors.generated.go b/common/mux/errors.generated.go new file mode 100644 index 00000000..9318ceac --- /dev/null +++ b/common/mux/errors.generated.go @@ -0,0 +1,9 @@ +package mux + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/mux/frame.go b/common/mux/frame.go index bdf5cc8c..30f3c1db 100644 --- a/common/mux/frame.go +++ b/common/mux/frame.go @@ -7,7 +7,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/bitmask" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" @@ -59,7 +58,6 @@ type FrameMetadata struct { SessionID uint16 Option bitmask.Byte SessionStatus SessionStatus - GlobalID [8]byte } func (f FrameMetadata) WriteTo(b *buf.Buffer) error { @@ -83,9 +81,6 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error { if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil { return err } - if b.UDP != nil { // make sure it's user's proxy request - b.Write(f.GlobalID[:]) // no need to check whether it's empty - } } else if b.UDP != nil { b.WriteByte(byte(TargetNetworkUDP)) addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port) @@ -103,7 +98,7 @@ func (f *FrameMetadata) Unmarshal(reader io.Reader) error { return err } if metaLen > 512 { - return errors.New("invalid metalen ", metaLen).AtError() + return newError("invalid metalen ", metaLen).AtError() } b := buf.New() @@ -119,7 +114,7 @@ func (f *FrameMetadata) Unmarshal(reader io.Reader) error { // Visible for testing only. func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error { if b.Len() < 4 { - return errors.New("insufficient buffer: ", b.Len()) + return newError("insufficient buffer: ", b.Len()) } f.SessionID = binary.BigEndian.Uint16(b.BytesTo(2)) @@ -127,17 +122,16 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error { f.Option = bitmask.Byte(b.Byte(3)) f.Target.Network = net.Network_Unknown - if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() > 4 && - TargetNetwork(b.Byte(4)) == TargetNetworkUDP) { // MUST check the flag first + if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() != 4) { if b.Len() < 8 { - return errors.New("insufficient buffer: ", b.Len()) + return newError("insufficient buffer: ", b.Len()) } network := TargetNetwork(b.Byte(4)) b.Advance(5) addr, port, err := addrParser.ReadAddressPort(nil, b) if err != nil { - return errors.New("failed to parse address and port").Base(err) + return newError("failed to parse address and port").Base(err) } switch network { @@ -146,15 +140,9 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error { case TargetNetworkUDP: f.Target = net.UDPDestination(addr, port) default: - return errors.New("unknown network type: ", network) + return newError("unknown network type: ", network) } } - // Application data is essential, to test whether the pipe is closed. - if f.SessionStatus == SessionStatusNew && f.Option.Has(OptionData) && - f.Target.Network == net.Network_UDP && b.Len() >= 8 { - copy(f.GlobalID[:], b.Bytes()) - } - return nil } diff --git a/common/mux/mux.go b/common/mux/mux.go index 707f4622..0eaebf51 100644 --- a/common/mux/mux.go +++ b/common/mux/mux.go @@ -1 +1,3 @@ package mux + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/common/mux/mux_test.go b/common/mux/mux_test.go index f326ffd7..39def2ab 100644 --- a/common/mux/mux_test.go +++ b/common/mux/mux_test.go @@ -32,13 +32,13 @@ func TestReaderWriter(t *testing.T) { pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024)) dest := net.TCPDestination(net.DomainAddress("example.com"), 80) - writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{}) + writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream) dest2 := net.TCPDestination(net.LocalHostIP, 443) - writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{}) + writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream) dest3 := net.TCPDestination(net.LocalHostIPv6, 18374) - writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{}) + writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream) writePayload := func(writer *Writer, payload ...byte) error { b := buf.New() diff --git a/common/mux/reader.go b/common/mux/reader.go index b9714cdf..31c008c3 100644 --- a/common/mux/reader.go +++ b/common/mux/reader.go @@ -5,7 +5,6 @@ import ( "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/crypto" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/serial" ) @@ -38,7 +37,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { } if size > buf.Size { - return nil, errors.New("packet size too large: ", size) + return nil, newError("packet size too large: ", size) } b := buf.New() diff --git a/common/mux/server.go b/common/mux/server.go index 8fc0ae09..df461be7 100644 --- a/common/mux/server.go +++ b/common/mux/server.go @@ -94,12 +94,12 @@ func NewServerWorker(ctx context.Context, d routing.Dispatcher, link *transport. func handle(ctx context.Context, s *Session, output buf.Writer) { writer := NewResponseWriter(s.ID, output, s.transferType) if err := buf.Copy(s.input, writer); err != nil { - errors.LogInfoInner(ctx, err, "session ", s.ID, " ends.") + newError("session ", s.ID, " ends.").Base(err).WriteToLog(session.ExportIDToError(ctx)) writer.hasError = true } writer.Close() - s.Close(false) + s.Close() } func (w *ServerWorker) ActiveConnections() uint32 { @@ -118,10 +118,7 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu } func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error { - // deep-clone outbounds because it is going to be mutated concurrently - // (Target and OriginalTarget) - ctx = session.ContextCloneOutboundsAndContent(ctx) - errors.LogInfo(ctx, "received request for ", meta.Target) + newError("received request for ", meta.Target).WriteToLog(session.ExportIDToError(ctx)) { msg := &log.AccessMessage{ To: meta.Target, @@ -134,87 +131,12 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, } ctx = log.ContextWithAccessMessage(ctx, msg) } - - if network := session.AllowedNetworkFromContext(ctx); network != net.Network_Unknown { - if meta.Target.Network != network { - return errors.New("unexpected network ", meta.Target.Network) // it will break the whole Mux connection - } - } - - if meta.GlobalID != [8]byte{} { // MUST ignore empty Global ID - mb, err := NewPacketReader(reader, &meta.Target).ReadMultiBuffer() - if err != nil { - return err - } - XUDPManager.Lock() - x := XUDPManager.Map[meta.GlobalID] - if x == nil { - x = &XUDP{GlobalID: meta.GlobalID} - XUDPManager.Map[meta.GlobalID] = x - XUDPManager.Unlock() - } else { - if x.Status == Initializing { // nearly impossible - XUDPManager.Unlock() - errors.LogWarningInner(ctx, errors.New("conflict"), "XUDP hit ", meta.GlobalID) - // It's not a good idea to return an err here, so just let client wait. - // Client will receive an End frame after sending a Keep frame. - return nil - } - x.Status = Initializing - XUDPManager.Unlock() - x.Mux.Close(false) // detach from previous Mux - b := buf.New() - b.Write(mb[0].Bytes()) - b.UDP = mb[0].UDP - if err = x.Mux.output.WriteMultiBuffer(mb); err != nil { - x.Interrupt() - mb = buf.MultiBuffer{b} - } else { - b.Release() - mb = nil - } - errors.LogInfoInner(ctx, err, "XUDP hit ", meta.GlobalID) - } - if mb != nil { - ctx = session.ContextWithTimeoutOnly(ctx, true) - // Actually, it won't return an error in Xray-core's implementations. - link, err := w.dispatcher.Dispatch(ctx, meta.Target) - if err != nil { - XUDPManager.Lock() - delete(XUDPManager.Map, x.GlobalID) - XUDPManager.Unlock() - err = errors.New("XUDP new ", meta.GlobalID).Base(errors.New("failed to dispatch request to ", meta.Target).Base(err)) - return err // it will break the whole Mux connection - } - link.Writer.WriteMultiBuffer(mb) // it's meaningless to test a new pipe - x.Mux = &Session{ - input: link.Reader, - output: link.Writer, - } - errors.LogInfoInner(ctx, err, "XUDP new ", meta.GlobalID) - } - x.Mux = &Session{ - input: x.Mux.input, - output: x.Mux.output, - parent: w.sessionManager, - ID: meta.SessionID, - transferType: protocol.TransferTypePacket, - XUDP: x, - } - go handle(ctx, x.Mux, w.link.Writer) - x.Status = Active - if !w.sessionManager.Add(x.Mux) { - x.Mux.Close(false) - } - return nil - } - link, err := w.dispatcher.Dispatch(ctx, meta.Target) if err != nil { if meta.Option.Has(OptionData) { buf.Copy(NewStreamReader(reader), buf.Discard) } - return errors.New("failed to dispatch request.").Base(err) + return newError("failed to dispatch request.").Base(err) } s := &Session{ input: link.Reader, @@ -235,7 +157,8 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, rr := s.NewReader(reader, &meta.Target) if err := buf.Copy(rr, s.output); err != nil { buf.Copy(rr, buf.Discard) - return s.Close(false) + common.Interrupt(s.input) + return s.Close() } return nil } @@ -258,9 +181,16 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere err := buf.Copy(rr, s.output) if err != nil && buf.IsWriteError(err) { - errors.LogInfoInner(context.Background(), err, "failed to write to downstream writer. closing session ", s.ID) - s.Close(false) - return buf.Copy(rr, buf.Discard) + newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog() + + // Notify remote peer to close this session. + closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream) + closingWriter.Close() + + drainErr := buf.Copy(rr, buf.Discard) + common.Interrupt(s.input) + s.Close() + return drainErr } return err @@ -268,7 +198,12 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { if s, found := w.sessionManager.Get(meta.SessionID); found { - s.Close(false) + if meta.Option.Has(OptionError) { + common.Interrupt(s.input) + common.Interrupt(s.output) + } + common.Interrupt(s.input) + s.Close() } if meta.Option.Has(OptionData) { return buf.Copy(NewStreamReader(reader), buf.Discard) @@ -280,7 +215,7 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead var meta FrameMetadata err := meta.Unmarshal(reader) if err != nil { - return errors.New("failed to read metadata").Base(err) + return newError("failed to read metadata").Base(err) } switch meta.SessionStatus { @@ -294,11 +229,11 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead err = w.handleStatusKeep(&meta, reader) default: status := meta.SessionStatus - return errors.New("unknown status: ", status).AtError() + return newError("unknown status: ", status).AtError() } if err != nil { - return errors.New("failed to process data").Base(err) + return newError("failed to process data").Base(err) } return nil } @@ -317,7 +252,7 @@ func (w *ServerWorker) run(ctx context.Context) { err := w.handleFrame(ctx, reader) if err != nil { if errors.Cause(err) != io.EOF { - errors.LogInfoInner(ctx, err, "unexpected EOF") + newError("unexpected EOF").Base(err).WriteToLog(session.ExportIDToError(ctx)) common.Interrupt(input) } return diff --git a/common/mux/server_test.go b/common/mux/server_test.go deleted file mode 100644 index 4158bf46..00000000 --- a/common/mux/server_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package mux_test - -import ( - "context" - "testing" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/mux" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/transport" - "github.com/xtls/xray-core/transport/pipe" -) - -func newLinkPair() (*transport.Link, *transport.Link) { - opt := pipe.WithoutSizeLimit() - uplinkReader, uplinkWriter := pipe.New(opt) - downlinkReader, downlinkWriter := pipe.New(opt) - - uplink := &transport.Link{ - Reader: uplinkReader, - Writer: downlinkWriter, - } - - downlink := &transport.Link{ - Reader: downlinkReader, - Writer: uplinkWriter, - } - - return uplink, downlink -} - -type TestDispatcher struct { - OnDispatch func(ctx context.Context, dest net.Destination) (*transport.Link, error) -} - -func (d *TestDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { - return d.OnDispatch(ctx, dest) -} - -func (d *TestDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error { - return nil -} - -func (d *TestDispatcher) Start() error { - return nil -} - -func (d *TestDispatcher) Close() error { - return nil -} - -func (*TestDispatcher) Type() interface{} { - return routing.DispatcherType() -} - -func TestRegressionOutboundLeak(t *testing.T) { - originalOutbounds := []*session.Outbound{{}} - serverCtx := session.ContextWithOutbounds(context.Background(), originalOutbounds) - - websiteUplink, websiteDownlink := newLinkPair() - - dispatcher := TestDispatcher{ - OnDispatch: func(ctx context.Context, dest net.Destination) (*transport.Link, error) { - // emulate what DefaultRouter.Dispatch does, and mutate something on the context - ob := session.OutboundsFromContext(ctx)[0] - ob.Target = dest - return websiteDownlink, nil - }, - } - - muxServerUplink, muxServerDownlink := newLinkPair() - _, err := mux.NewServerWorker(serverCtx, &dispatcher, muxServerUplink) - common.Must(err) - - client, err := mux.NewClientWorker(*muxServerDownlink, mux.ClientStrategy{}) - common.Must(err) - - clientCtx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ - Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80), - }}) - - muxClientUplink, muxClientDownlink := newLinkPair() - - ok := client.Dispatch(clientCtx, muxClientUplink) - if !ok { - t.Error("failed to dispatch") - } - - { - b := buf.FromBytes([]byte("hello")) - common.Must(muxClientDownlink.Writer.WriteMultiBuffer(buf.MultiBuffer{b})) - } - - resMb, err := websiteUplink.Reader.ReadMultiBuffer() - common.Must(err) - res := resMb.String() - if res != "hello" { - t.Error("upload: ", res) - } - - { - b := buf.FromBytes([]byte("world")) - common.Must(websiteUplink.Writer.WriteMultiBuffer(buf.MultiBuffer{b})) - } - - resMb, err = muxClientDownlink.Reader.ReadMultiBuffer() - common.Must(err) - res = resMb.String() - if res != "world" { - t.Error("download: ", res) - } - - outbounds := session.OutboundsFromContext(serverCtx) - if outbounds[0] != originalOutbounds[0] { - t.Error("outbound got reassigned: ", outbounds[0]) - } - - if outbounds[0].Target.Address != nil { - t.Error("outbound target got leaked: ", outbounds[0].Target.String()) - } -} diff --git a/common/mux/session.go b/common/mux/session.go index 5e4b69ca..2f21b97a 100644 --- a/common/mux/session.go +++ b/common/mux/session.go @@ -1,18 +1,12 @@ package mux import ( - "context" - "io" - "runtime" "sync" - "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/transport/pipe" ) type SessionManager struct { @@ -67,25 +61,21 @@ func (m *SessionManager) Allocate() *Session { return s } -func (m *SessionManager) Add(s *Session) bool { +func (m *SessionManager) Add(s *Session) { m.Lock() defer m.Unlock() if m.closed { - return false + return } m.count++ m.sessions[s.ID] = s - return true } -func (m *SessionManager) Remove(locked bool, id uint16) { - if !locked { - m.Lock() - defer m.Unlock() - } - locked = true +func (m *SessionManager) Remove(id uint16) { + m.Lock() + defer m.Unlock() if m.closed { return @@ -93,11 +83,9 @@ func (m *SessionManager) Remove(locked bool, id uint16) { delete(m.sessions, id) - /* - if len(m.sessions) == 0 { - m.sessions = make(map[uint16]*Session, 16) - } - */ + if len(m.sessions) == 0 { + m.sessions = make(map[uint16]*Session, 16) + } } func (m *SessionManager) Get(id uint16) (*Session, bool) { @@ -139,7 +127,8 @@ func (m *SessionManager) Close() error { m.closed = true for _, s := range m.sessions { - s.Close(true) + common.Close(s.input) + common.Close(s.output) } m.sessions = nil @@ -153,40 +142,13 @@ type Session struct { parent *SessionManager ID uint16 transferType protocol.TransferType - closed bool - XUDP *XUDP } // Close closes all resources associated with this session. -func (s *Session) Close(locked bool) error { - if !locked { - s.parent.Lock() - defer s.parent.Unlock() - } - locked = true - if s.closed { - return nil - } - s.closed = true - if s.XUDP == nil { - common.Interrupt(s.input) - common.Close(s.output) - } else { - // Stop existing handle(), then trigger writer.Close(). - // Note that s.output may be dispatcher.SizeStatWriter. - s.input.(*pipe.Reader).ReturnAnError(io.EOF) - runtime.Gosched() - // If the error set by ReturnAnError still exists, clear it. - s.input.(*pipe.Reader).Recover() - XUDPManager.Lock() - if s.XUDP.Status == Active { - s.XUDP.Expire = time.Now().Add(time.Minute) - s.XUDP.Status = Expiring - errors.LogDebug(context.Background(), "XUDP put ", s.XUDP.GlobalID) - } - XUDPManager.Unlock() - } - s.parent.Remove(locked, s.ID) +func (s *Session) Close() error { + common.Close(s.output) + common.Close(s.input) + s.parent.Remove(s.ID) return nil } @@ -197,45 +159,3 @@ func (s *Session) NewReader(reader *buf.BufferedReader, dest *net.Destination) b } return NewPacketReader(reader, dest) } - -const ( - Initializing = 0 - Active = 1 - Expiring = 2 -) - -type XUDP struct { - GlobalID [8]byte - Status uint64 - Expire time.Time - Mux *Session -} - -func (x *XUDP) Interrupt() { - common.Interrupt(x.Mux.input) - common.Close(x.Mux.output) -} - -var XUDPManager struct { - sync.Mutex - Map map[[8]byte]*XUDP -} - -func init() { - XUDPManager.Map = make(map[[8]byte]*XUDP) - go func() { - for { - time.Sleep(time.Minute) - now := time.Now() - XUDPManager.Lock() - for id, x := range XUDPManager.Map { - if x.Status == Expiring && now.After(x.Expire) { - x.Interrupt() - delete(XUDPManager.Map, id) - errors.LogDebug(context.Background(), "XUDP del ", id) - } - } - XUDPManager.Unlock() - } - }() -} diff --git a/common/mux/session_test.go b/common/mux/session_test.go index d81ad8c4..7139df10 100644 --- a/common/mux/session_test.go +++ b/common/mux/session_test.go @@ -44,7 +44,7 @@ func TestSessionManagerClose(t *testing.T) { if m.CloseIfNoSession() { t.Error("able to close") } - m.Remove(false, s.ID) + m.Remove(s.ID) if !m.CloseIfNoSession() { t.Error("not able to close") } diff --git a/common/mux/writer.go b/common/mux/writer.go index a6dc551d..f7a22b2d 100644 --- a/common/mux/writer.go +++ b/common/mux/writer.go @@ -15,17 +15,15 @@ type Writer struct { followup bool hasError bool transferType protocol.TransferType - globalID [8]byte } -func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType, globalID [8]byte) *Writer { +func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType) *Writer { return &Writer{ id: id, dest: dest, writer: writer, followup: false, transferType: transferType, - globalID: globalID, } } @@ -42,7 +40,6 @@ func (w *Writer) getNextFrameMeta() FrameMetadata { meta := FrameMetadata{ SessionID: w.id, Target: w.dest, - GlobalID: w.globalID, } if w.followup { diff --git a/common/net/address.go b/common/net/address.go index 1567e4d1..9ba8c473 100644 --- a/common/net/address.go +++ b/common/net/address.go @@ -2,11 +2,8 @@ package net import ( "bytes" - "context" "net" "strings" - - "github.com/xtls/xray-core/common/errors" ) var ( @@ -115,17 +112,12 @@ func IPAddress(ip []byte) Address { } return addr default: - errors.LogError(context.Background(), "invalid IP format: ", ip) + newError("invalid IP format: ", ip).AtError().WriteToLog() return nil } } // DomainAddress creates an Address with given domain. -// This is an internal function that forcibly converts a string to domain. -// It's mainly used in test files and mux. -// Unless you have a specific reason, use net.ParseAddress instead, -// as this function does not check whether the input is an IP address. -// Otherwise, you will get strange results like domain: 1.1.1.1 func DomainAddress(domain string) Address { return domainAddress(domain) } diff --git a/common/net/address.pb.go b/common/net/address.pb.go index 4ca29cbe..5757a018 100644 --- a/common/net/address.pb.go +++ b/common/net/address.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/net/address.proto package net @@ -36,9 +36,11 @@ type IPOrDomain struct { func (x *IPOrDomain) Reset() { *x = IPOrDomain{} - mi := &file_common_net_address_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_net_address_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *IPOrDomain) String() string { @@ -49,7 +51,7 @@ func (*IPOrDomain) ProtoMessage() {} func (x *IPOrDomain) ProtoReflect() protoreflect.Message { mi := &file_common_net_address_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -134,7 +136,7 @@ func file_common_net_address_proto_rawDescGZIP() []byte { } var file_common_net_address_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_common_net_address_proto_goTypes = []any{ +var file_common_net_address_proto_goTypes = []interface{}{ (*IPOrDomain)(nil), // 0: xray.common.net.IPOrDomain } var file_common_net_address_proto_depIdxs = []int32{ @@ -150,7 +152,21 @@ func file_common_net_address_proto_init() { if File_common_net_address_proto != nil { return } - file_common_net_address_proto_msgTypes[0].OneofWrappers = []any{ + if !protoimpl.UnsafeEnabled { + file_common_net_address_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IPOrDomain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_common_net_address_proto_msgTypes[0].OneofWrappers = []interface{}{ (*IPOrDomain_Ip)(nil), (*IPOrDomain_Domain)(nil), } diff --git a/common/net/cnc/connection.go b/common/net/cnc/connection.go index bdae5409..42b632e7 100644 --- a/common/net/cnc/connection.go +++ b/common/net/cnc/connection.go @@ -51,8 +51,8 @@ func ConnectionOutputMulti(reader buf.Reader) ConnectionOption { func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption { return func(c *connection) { c.reader = &buf.BufferedReader{ - Reader: reader, - Splitter: buf.SplitFirstBytes, + Reader: reader, + Spliter: buf.SplitFirstBytes, } } } diff --git a/common/net/destination.go b/common/net/destination.go index 90f8298b..055395e9 100644 --- a/common/net/destination.go +++ b/common/net/destination.go @@ -97,35 +97,6 @@ func (d Destination) NetAddr() string { return addr } -// RawNetAddr converts a net.Addr from its Destination presentation. -func (d Destination) RawNetAddr() net.Addr { - var addr net.Addr - switch d.Network { - case Network_TCP: - if d.Address.Family().IsIP() { - addr = &net.TCPAddr{ - IP: d.Address.IP(), - Port: int(d.Port), - } - } - case Network_UDP: - if d.Address.Family().IsIP() { - addr = &net.UDPAddr{ - IP: d.Address.IP(), - Port: int(d.Port), - } - } - case Network_UNIX: - if d.Address.Family().IsDomain() { - addr = &net.UnixAddr{ - Name: d.Address.String(), - Net: d.Network.SystemString(), - } - } - } - return addr -} - // String returns the strings form of this Destination. func (d Destination) String() string { prefix := "unknown:" diff --git a/common/net/destination.pb.go b/common/net/destination.pb.go index b5f27023..2d557b84 100644 --- a/common/net/destination.pb.go +++ b/common/net/destination.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/net/destination.proto package net @@ -33,9 +33,11 @@ type Endpoint struct { func (x *Endpoint) Reset() { *x = Endpoint{} - mi := &file_common_net_destination_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_net_destination_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Endpoint) String() string { @@ -46,7 +48,7 @@ func (*Endpoint) ProtoMessage() {} func (x *Endpoint) ProtoReflect() protoreflect.Message { mi := &file_common_net_destination_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -121,7 +123,7 @@ func file_common_net_destination_proto_rawDescGZIP() []byte { } var file_common_net_destination_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_common_net_destination_proto_goTypes = []any{ +var file_common_net_destination_proto_goTypes = []interface{}{ (*Endpoint)(nil), // 0: xray.common.net.Endpoint (Network)(0), // 1: xray.common.net.Network (*IPOrDomain)(nil), // 2: xray.common.net.IPOrDomain @@ -143,6 +145,20 @@ func file_common_net_destination_proto_init() { } file_common_net_network_proto_init() file_common_net_address_proto_init() + if !protoimpl.UnsafeEnabled { + file_common_net_destination_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Endpoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/common/net/errors.generated.go b/common/net/errors.generated.go new file mode 100644 index 00000000..fb64eac5 --- /dev/null +++ b/common/net/errors.generated.go @@ -0,0 +1,9 @@ +package net + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/net/net.go b/common/net/net.go index 92431c42..5027617c 100644 --- a/common/net/net.go +++ b/common/net/net.go @@ -1,14 +1,4 @@ // Package net is a drop-in replacement to Golang's net package, with some more functionalities. package net // import "github.com/xtls/xray-core/common/net" -import "time" - -// defines the maximum time an idle TCP session can survive in the tunnel, so -// it should be consistent across HTTP versions and with other transports. -const ConnIdleTimeout = 300 * time.Second - -// consistent with quic-go -const QuicgoH3KeepAlivePeriod = 10 * time.Second - -// consistent with chrome -const ChromeH2KeepAlivePeriod = 45 * time.Second +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/common/net/network.pb.go b/common/net/network.pb.go index 9d55b336..699557f7 100644 --- a/common/net/network.pb.go +++ b/common/net/network.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/net/network.proto package net @@ -24,21 +24,25 @@ type Network int32 const ( Network_Unknown Network = 0 - Network_TCP Network = 2 - Network_UDP Network = 3 - Network_UNIX Network = 4 + // Deprecated: Do not use. + Network_RawTCP Network = 1 + Network_TCP Network = 2 + Network_UDP Network = 3 + Network_UNIX Network = 4 ) // Enum value maps for Network. var ( Network_name = map[int32]string{ 0: "Unknown", + 1: "RawTCP", 2: "TCP", 3: "UDP", 4: "UNIX", } Network_value = map[string]int32{ "Unknown": 0, + "RawTCP": 1, "TCP": 2, "UDP": 3, "UNIX": 4, @@ -83,9 +87,11 @@ type NetworkList struct { func (x *NetworkList) Reset() { *x = NetworkList{} - mi := &file_common_net_network_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_net_network_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *NetworkList) String() string { @@ -96,7 +102,7 @@ func (*NetworkList) ProtoMessage() {} func (x *NetworkList) ProtoReflect() protoreflect.Message { mi := &file_common_net_network_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -127,9 +133,10 @@ var file_common_net_network_proto_rawDesc = []byte{ 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2a, 0x32, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2a, 0x42, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, - 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, + 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x06, 0x52, 0x61, 0x77, 0x54, 0x43, 0x50, + 0x10, 0x01, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x4e, 0x49, 0x58, 0x10, 0x04, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, @@ -153,7 +160,7 @@ func file_common_net_network_proto_rawDescGZIP() []byte { var file_common_net_network_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_common_net_network_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_common_net_network_proto_goTypes = []any{ +var file_common_net_network_proto_goTypes = []interface{}{ (Network)(0), // 0: xray.common.net.Network (*NetworkList)(nil), // 1: xray.common.net.NetworkList } @@ -171,6 +178,20 @@ func file_common_net_network_proto_init() { if File_common_net_network_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_common_net_network_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/common/net/network.proto b/common/net/network.proto index e7579a21..f2487fbc 100644 --- a/common/net/network.proto +++ b/common/net/network.proto @@ -9,6 +9,7 @@ option java_multiple_files = true; enum Network { Unknown = 0; + RawTCP = 1 [deprecated = true]; TCP = 2; UDP = 3; UNIX = 4; diff --git a/common/net/port.go b/common/net/port.go index d4a6514c..2a0bf637 100644 --- a/common/net/port.go +++ b/common/net/port.go @@ -3,8 +3,6 @@ package net import ( "encoding/binary" "strconv" - - "github.com/xtls/xray-core/common/errors" ) // Port represents a network port in TCP and UDP protocol. @@ -20,7 +18,7 @@ func PortFromBytes(port []byte) Port { // @error when the integer is not positive or larger then 65535 func PortFromInt(val uint32) (Port, error) { if val > 65535 { - return Port(0), errors.New("invalid port range: ", val) + return Port(0), newError("invalid port range: ", val) } return Port(val), nil } @@ -30,7 +28,7 @@ func PortFromInt(val uint32) (Port, error) { func PortFromString(s string) (Port, error) { val, err := strconv.ParseUint(s, 10, 32) if err != nil { - return Port(0), errors.New("invalid port range: ", s) + return Port(0), newError("invalid port range: ", s) } return PortFromInt(uint32(val)) } diff --git a/common/net/port.pb.go b/common/net/port.pb.go index d6042685..cae70bc9 100644 --- a/common/net/port.pb.go +++ b/common/net/port.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/net/port.proto package net @@ -34,9 +34,11 @@ type PortRange struct { func (x *PortRange) Reset() { *x = PortRange{} - mi := &file_common_net_port_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_net_port_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *PortRange) String() string { @@ -47,7 +49,7 @@ func (*PortRange) ProtoMessage() {} func (x *PortRange) ProtoReflect() protoreflect.Message { mi := &file_common_net_port_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -87,9 +89,11 @@ type PortList struct { func (x *PortList) Reset() { *x = PortList{} - mi := &file_common_net_port_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_net_port_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *PortList) String() string { @@ -100,7 +104,7 @@ func (*PortList) ProtoMessage() {} func (x *PortList) ProtoReflect() protoreflect.Message { mi := &file_common_net_port_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -155,7 +159,7 @@ func file_common_net_port_proto_rawDescGZIP() []byte { } var file_common_net_port_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_common_net_port_proto_goTypes = []any{ +var file_common_net_port_proto_goTypes = []interface{}{ (*PortRange)(nil), // 0: xray.common.net.PortRange (*PortList)(nil), // 1: xray.common.net.PortList } @@ -173,6 +177,32 @@ func file_common_net_port_proto_init() { if File_common_net_port_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_common_net_port_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PortRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_net_port_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PortList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/common/net/system.go b/common/net/system.go index 7e1c4b01..e5bded04 100644 --- a/common/net/system.go +++ b/common/net/system.go @@ -76,9 +76,8 @@ type ( ) var ( - ResolveTCPAddr = net.ResolveTCPAddr - ResolveUDPAddr = net.ResolveUDPAddr ResolveUnixAddr = net.ResolveUnixAddr + ResolveUDPAddr = net.ResolveUDPAddr ) type Resolver = net.Resolver diff --git a/common/ocsp/errors.generated.go b/common/ocsp/errors.generated.go new file mode 100644 index 00000000..4bdbc25c --- /dev/null +++ b/common/ocsp/errors.generated.go @@ -0,0 +1,9 @@ +package ocsp + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/ocsp/ocsp.go b/common/ocsp/ocsp.go index d67670af..02140c0d 100644 --- a/common/ocsp/ocsp.go +++ b/common/ocsp/ocsp.go @@ -8,7 +8,6 @@ import ( "net/http" "os" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/platform/filesystem" "golang.org/x/crypto/ocsp" ) @@ -29,9 +28,6 @@ func GetOCSPStapling(cert [][]byte, path string) ([]byte, error) { ocspData, err := GetOCSPForFile(path) if err != nil { ocspData, err = GetOCSPForCert(cert) - if err != nil { - return nil, err - } if !CheckOCSPFileIsNotExist(path) { err = os.Remove(path) if err != nil { @@ -64,26 +60,26 @@ func GetOCSPForCert(cert [][]byte) ([]byte, error) { } issuedCert := certificates[0] if len(issuedCert.OCSPServer) == 0 { - return nil, errors.New("no OCSP server specified in cert") + return nil, newError("no OCSP server specified in cert") } if len(certificates) == 1 { if len(issuedCert.IssuingCertificateURL) == 0 { - return nil, errors.New("no issuing certificate URL") + return nil, newError("no issuing certificate URL") } resp, errC := http.Get(issuedCert.IssuingCertificateURL[0]) if errC != nil { - return nil, errors.New("no issuing certificate URL") + return nil, newError("no issuing certificate URL") } defer resp.Body.Close() issuerBytes, errC := io.ReadAll(resp.Body) if errC != nil { - return nil, errors.New(errC) + return nil, newError(errC) } issuerCert, errC := x509.ParseCertificate(issuerBytes) if errC != nil { - return nil, errors.New(errC) + return nil, newError(errC) } certificates = append(certificates, issuerCert) @@ -97,12 +93,12 @@ func GetOCSPForCert(cert [][]byte) ([]byte, error) { reader := bytes.NewReader(ocspReq) req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader) if err != nil { - return nil, errors.New(err) + return nil, newError(err) } defer req.Body.Close() ocspResBytes, err := io.ReadAll(req.Body) if err != nil { - return nil, errors.New(err) + return nil, newError(err) } return ocspResBytes, nil } @@ -129,7 +125,7 @@ func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) { } if len(certificates) == 0 { - return nil, errors.New("no certificates were found while parsing the bundle") + return nil, newError("no certificates were found while parsing the bundle") } return certificates, nil diff --git a/common/platform/ctlcmd/ctlcmd.go b/common/platform/ctlcmd/ctlcmd.go index bc28ace1..ad9550fa 100644 --- a/common/platform/ctlcmd/ctlcmd.go +++ b/common/platform/ctlcmd/ctlcmd.go @@ -1,21 +1,21 @@ package ctlcmd import ( - "context" "io" "os" "os/exec" "strings" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/platform" ) +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + func Run(args []string, input io.Reader) (buf.MultiBuffer, error) { xctl := platform.GetToolLocation("xctl") if _, err := os.Stat(xctl); err != nil { - return nil, errors.New("xctl doesn't exist").Base(err) + return nil, newError("xctl doesn't exist").Base(err) } var errBuffer buf.MultiBufferContainer @@ -30,7 +30,7 @@ func Run(args []string, input io.Reader) (buf.MultiBuffer, error) { } if err := cmd.Start(); err != nil { - return nil, errors.New("failed to start xctl").Base(err) + return nil, newError("failed to start xctl").Base(err) } if err := cmd.Wait(); err != nil { @@ -38,12 +38,12 @@ func Run(args []string, input io.Reader) (buf.MultiBuffer, error) { if errBuffer.Len() > 0 { msg += ": \n" + strings.TrimSpace(errBuffer.MultiBuffer.String()) } - return nil, errors.New(msg).Base(err) + return nil, newError(msg).Base(err) } // log stderr, info message if !errBuffer.IsEmpty() { - errors.LogInfo(context.Background(), " \n", strings.TrimSpace(errBuffer.MultiBuffer.String())) + newError(" \n", strings.TrimSpace(errBuffer.MultiBuffer.String())).AtInfo().WriteToLog() } return outBuffer.MultiBuffer, nil diff --git a/common/platform/ctlcmd/errors.generated.go b/common/platform/ctlcmd/errors.generated.go new file mode 100644 index 00000000..c861825c --- /dev/null +++ b/common/platform/ctlcmd/errors.generated.go @@ -0,0 +1,9 @@ +package ctlcmd + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/platform/filesystem/file.go b/common/platform/filesystem/file.go index e4fe2a9a..e10bfc11 100644 --- a/common/platform/filesystem/file.go +++ b/common/platform/filesystem/file.go @@ -3,7 +3,6 @@ package filesystem import ( "io" "os" - "path/filepath" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/platform" @@ -29,13 +28,6 @@ func ReadAsset(file string) ([]byte, error) { return ReadFile(platform.GetAssetLocation(file)) } -func ReadCert(file string) ([]byte, error) { - if filepath.IsAbs(file) { - return ReadFile(file) - } - return ReadFile(platform.GetCertLocation(file)) -} - func CopyFile(dst string, src string) error { bytes, err := ReadFile(src) if err != nil { diff --git a/common/platform/others.go b/common/platform/others.go index a405ac48..ff45a054 100644 --- a/common/platform/others.go +++ b/common/platform/others.go @@ -17,13 +17,15 @@ func LineSeparator() string { } func GetToolLocation(file string) string { - toolPath := NewEnvFlag(ToolLocation).GetValue(getExecutableDir) + const name = "xray.location.tool" + toolPath := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecutableDir) return filepath.Join(toolPath, file) } -// GetAssetLocation searches for `file` in the env dir, the executable dir, and certain locations +// GetAssetLocation searches for `file` in certain locations func GetAssetLocation(file string) string { - assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir) + const name = "xray.location.asset" + assetPath := NewEnvFlag(name).GetValue(getExecutableDir) defPath := filepath.Join(assetPath, file) for _, p := range []string{ defPath, @@ -42,9 +44,3 @@ func GetAssetLocation(file string) string { // asset not found, let the caller throw out the error return defPath } - -// GetCertLocation searches for `file` in the env dir and the executable dir -func GetCertLocation(file string) string { - certPath := NewEnvFlag(CertLocation).GetValue(getExecutableDir) - return filepath.Join(certPath, file) -} diff --git a/common/platform/platform.go b/common/platform/platform.go index b865dc0d..d5149db4 100644 --- a/common/platform/platform.go +++ b/common/platform/platform.go @@ -7,25 +7,6 @@ import ( "strings" ) -const ( - PluginLocation = "xray.location.plugin" - ConfigLocation = "xray.location.config" - ConfdirLocation = "xray.location.confdir" - ToolLocation = "xray.location.tool" - AssetLocation = "xray.location.asset" - CertLocation = "xray.location.cert" - - UseReadV = "xray.buf.readv" - UseFreedomSplice = "xray.buf.splice" - UseVmessPadding = "xray.vmess.padding" - UseCone = "xray.cone.disabled" - - BufferSize = "xray.ray.buffer.size" - BrowserDialerAddress = "xray.browser.dialer" - XUDPLog = "xray.xudp.show" - XUDPBaseKey = "xray.xudp.basekey" -) - type EnvFlag struct { Name string AltName string @@ -86,17 +67,20 @@ func getExecutableSubDir(dir string) func() string { } func GetPluginDirectory() string { - pluginDir := NewEnvFlag(PluginLocation).GetValue(getExecutableSubDir("plugins")) + const name = "xray.location.plugin" + pluginDir := NewEnvFlag(name).GetValue(getExecutableSubDir("plugins")) return pluginDir } func GetConfigurationPath() string { - configPath := NewEnvFlag(ConfigLocation).GetValue(getExecutableDir) + const name = "xray.location.config" + configPath := NewEnvFlag(name).GetValue(getExecutableDir) return filepath.Join(configPath, "config.json") } // GetConfDirPath reads "xray.location.confdir" func GetConfDirPath() string { - configPath := NewEnvFlag(ConfdirLocation).GetValue(func() string { return "" }) + const name = "xray.location.confdir" + configPath := NewEnvFlag(name).GetValue(func() string { return "" }) return configPath } diff --git a/common/platform/windows.go b/common/platform/windows.go index cb25a1ad..a568d5ae 100644 --- a/common/platform/windows.go +++ b/common/platform/windows.go @@ -15,18 +15,14 @@ func LineSeparator() string { } func GetToolLocation(file string) string { - toolPath := NewEnvFlag(ToolLocation).GetValue(getExecutableDir) + const name = "xray.location.tool" + toolPath := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecutableDir) return filepath.Join(toolPath, file+".exe") } -// GetAssetLocation searches for `file` in the env dir and the executable dir +// GetAssetLocation searches for `file` in the excutable dir func GetAssetLocation(file string) string { - assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir) + const name = "xray.location.asset" + assetPath := NewEnvFlag(name).GetValue(getExecutableDir) return filepath.Join(assetPath, file) } - -// GetCertLocation searches for `file` in the env dir and the executable dir -func GetCertLocation(file string) string { - certPath := NewEnvFlag(CertLocation).GetValue(getExecutableDir) - return filepath.Join(certPath, file) -} diff --git a/common/protocol/account.go b/common/protocol/account.go index 75568817..7793974a 100644 --- a/common/protocol/account.go +++ b/common/protocol/account.go @@ -1,11 +1,8 @@ package protocol -import "google.golang.org/protobuf/proto" - // Account is a user identity used for authentication. type Account interface { Equals(Account) bool - ToProto() proto.Message } // AsAccount is an object can be converted into account. diff --git a/common/protocol/address.go b/common/protocol/address.go index 0dcb8165..cf52a380 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -5,7 +5,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/serial" ) @@ -182,12 +181,12 @@ func (p *addressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres } if addrType >= 16 { - return nil, errors.New("unknown address type: ", addrType) + return nil, newError("unknown address type: ", addrType) } addrFamily := p.addrTypeMap[addrType] if addrFamily == net.AddressFamily(afInvalid) { - return nil, errors.New("unknown address type: ", addrType) + return nil, newError("unknown address type: ", addrType) } switch addrFamily { @@ -217,7 +216,7 @@ func (p *addressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres } } if !isValidDomain(domain) { - return nil, errors.New("invalid domain name: ", domain) + return nil, newError("invalid domain name: ", domain) } return net.DomainAddress(domain), nil default: @@ -228,7 +227,7 @@ func (p *addressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres func (p *addressParser) writeAddress(writer io.Writer, address net.Address) error { tb := p.addrByteMap[address.Family()] if tb == afInvalid { - return errors.New("unknown address family", address.Family()) + return newError("unknown address family", address.Family()) } switch address.Family() { @@ -242,7 +241,7 @@ func (p *addressParser) writeAddress(writer io.Writer, address net.Address) erro case net.AddressFamilyDomain: domain := address.Domain() if isDomainTooLong(domain) { - return errors.New("Super long domain is not supported: ", domain) + return newError("Super long domain is not supported: ", domain) } if _, err := writer.Write([]byte{tb, byte(len(domain))}); err != nil { diff --git a/common/protocol/dns/errors.generated.go b/common/protocol/dns/errors.generated.go new file mode 100644 index 00000000..d7375a9b --- /dev/null +++ b/common/protocol/dns/errors.generated.go @@ -0,0 +1,9 @@ +package dns + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/protocol/dns/io.go b/common/protocol/dns/io.go index dd88b61d..0c215a70 100644 --- a/common/protocol/dns/io.go +++ b/common/protocol/dns/io.go @@ -6,7 +6,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/serial" "golang.org/x/net/dns/dnsmessage" ) @@ -97,7 +96,7 @@ func (r *TCPReader) ReadMessage() (*buf.Buffer, error) { return nil, err } if size > buf.Size { - return nil, errors.New("message size too large: ", size) + return nil, newError("message size too large: ", size) } b := buf.New() if _, err := b.ReadFullFrom(r.reader, int32(size)); err != nil { diff --git a/common/protocol/errors.generated.go b/common/protocol/errors.generated.go new file mode 100644 index 00000000..694dd667 --- /dev/null +++ b/common/protocol/errors.generated.go @@ -0,0 +1,9 @@ +package protocol + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/protocol/headers.go b/common/protocol/headers.go index 261e21d9..8806ee80 100644 --- a/common/protocol/headers.go +++ b/common/protocol/headers.go @@ -3,10 +3,11 @@ package protocol import ( "runtime" + "golang.org/x/sys/cpu" + "github.com/xtls/xray-core/common/bitmask" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/uuid" - "golang.org/x/sys/cpu" ) // RequestCommand is a custom command in a proxy request. @@ -30,10 +31,11 @@ func (c RequestCommand) TransferType() TransferType { } const ( - // [DEPRECATED 2023-06] RequestOptionChunkStream indicates request payload is chunked. Each chunk consists of length, authentication and payload. + // RequestOptionChunkStream indicates request payload is chunked. Each chunk consists of length, authentication and payload. RequestOptionChunkStream bitmask.Byte = 0x01 - // 0x02 legacy setting + // RequestOptionConnectionReuse indicates client side expects to reuse the connection. + RequestOptionConnectionReuse bitmask.Byte = 0x02 RequestOptionChunkMasking bitmask.Byte = 0x04 @@ -75,6 +77,7 @@ type CommandSwitchAccount struct { Port net.Port ID uuid.UUID Level uint32 + AlterIds uint16 ValidMin byte } diff --git a/common/protocol/headers.pb.go b/common/protocol/headers.pb.go index 5dca8aa2..4096d56f 100644 --- a/common/protocol/headers.pb.go +++ b/common/protocol/headers.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/protocol/headers.proto package protocol @@ -24,10 +24,11 @@ type SecurityType int32 const ( SecurityType_UNKNOWN SecurityType = 0 + SecurityType_LEGACY SecurityType = 1 SecurityType_AUTO SecurityType = 2 SecurityType_AES128_GCM SecurityType = 3 SecurityType_CHACHA20_POLY1305 SecurityType = 4 - SecurityType_NONE SecurityType = 5 // [DEPRECATED 2023-06] + SecurityType_NONE SecurityType = 5 SecurityType_ZERO SecurityType = 6 ) @@ -35,6 +36,7 @@ const ( var ( SecurityType_name = map[int32]string{ 0: "UNKNOWN", + 1: "LEGACY", 2: "AUTO", 3: "AES128_GCM", 4: "CHACHA20_POLY1305", @@ -43,6 +45,7 @@ var ( } SecurityType_value = map[string]int32{ "UNKNOWN": 0, + "LEGACY": 1, "AUTO": 2, "AES128_GCM": 3, "CHACHA20_POLY1305": 4, @@ -88,9 +91,11 @@ type SecurityConfig struct { func (x *SecurityConfig) Reset() { *x = SecurityConfig{} - mi := &file_common_protocol_headers_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_protocol_headers_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SecurityConfig) String() string { @@ -101,7 +106,7 @@ func (*SecurityConfig) ProtoMessage() {} func (x *SecurityConfig) ProtoReflect() protoreflect.Message { mi := &file_common_protocol_headers_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -134,19 +139,20 @@ var file_common_protocol_headers_proto_rawDesc = []byte{ 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, - 0x60, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, - 0x41, 0x55, 0x54, 0x4f, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x45, 0x53, 0x31, 0x32, 0x38, - 0x5f, 0x47, 0x43, 0x4d, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, - 0x32, 0x30, 0x5f, 0x50, 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x04, 0x12, 0x08, 0x0a, - 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x45, 0x52, 0x4f, 0x10, - 0x06, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 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, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, - 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f, + 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x45, 0x53, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d, + 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50, + 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x45, 0x52, 0x4f, 0x10, 0x06, 0x42, 0x5e, 0x0a, + 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 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, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -163,7 +169,7 @@ func file_common_protocol_headers_proto_rawDescGZIP() []byte { var file_common_protocol_headers_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_common_protocol_headers_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_common_protocol_headers_proto_goTypes = []any{ +var file_common_protocol_headers_proto_goTypes = []interface{}{ (SecurityType)(0), // 0: xray.common.protocol.SecurityType (*SecurityConfig)(nil), // 1: xray.common.protocol.SecurityConfig } @@ -181,6 +187,20 @@ func file_common_protocol_headers_proto_init() { if File_common_protocol_headers_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_common_protocol_headers_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SecurityConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/common/protocol/headers.proto b/common/protocol/headers.proto index 1ae3537f..cb0b8ff0 100644 --- a/common/protocol/headers.proto +++ b/common/protocol/headers.proto @@ -8,10 +8,11 @@ option java_multiple_files = true; enum SecurityType { UNKNOWN = 0; + LEGACY = 1; AUTO = 2; AES128_GCM = 3; CHACHA20_POLY1305 = 4; - NONE = 5; // [DEPRECATED 2023-06] + NONE = 5; ZERO = 6; } diff --git a/common/protocol/http/sniff.go b/common/protocol/http/sniff.go index e85a0792..ceedede2 100644 --- a/common/protocol/http/sniff.go +++ b/common/protocol/http/sniff.go @@ -2,13 +2,11 @@ package http import ( "bytes" - "context" "errors" "strings" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/session" ) type version byte @@ -58,14 +56,7 @@ func beginWithHTTPMethod(b []byte) error { return errNotHTTPMethod } -func SniffHTTP(b []byte, c context.Context) (*SniffHeader, error) { - content := session.ContentFromContext(c) - ShouldSniffAttr := true - // If content.Attributes have information, that means it comes from HTTP inbound PlainHTTP mode. - // It will set attributes, so skip it. - if content == nil || len(content.Attributes) != 0 { - ShouldSniffAttr = false - } +func SniffHTTP(b []byte) (*SniffHeader, error) { if err := beginWithHTTPMethod(b); err != nil { return nil, err } @@ -85,12 +76,8 @@ func SniffHTTP(b []byte, c context.Context) (*SniffHeader, error) { continue } key := strings.ToLower(string(parts[0])) - value := string(bytes.TrimSpace(parts[1])) - if ShouldSniffAttr { - content.SetAttribute(key, value) // Put header in attribute - } if key == "host" { - rawHost := strings.ToLower(value) + rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1]))) dest, err := ParseHost(rawHost, net.Port(80)) if err != nil { return nil, err @@ -98,16 +85,6 @@ func SniffHTTP(b []byte, c context.Context) (*SniffHeader, error) { sh.host = dest.Address.String() } } - // Parse request line - // Request line is like this - // "GET /homo/114514 HTTP/1.1" - if len(headers) > 0 && ShouldSniffAttr { - RequestLineParts := bytes.Split(headers[0], []byte{' '}) - if len(RequestLineParts) == 3 { - content.SetAttribute(":method", string(RequestLineParts[0])) - content.SetAttribute(":path", string(RequestLineParts[1])) - } - } if len(sh.host) > 0 { return sh, nil diff --git a/common/protocol/http/sniff_test.go b/common/protocol/http/sniff_test.go index 09ce7d6c..fff66415 100644 --- a/common/protocol/http/sniff_test.go +++ b/common/protocol/http/sniff_test.go @@ -1,7 +1,6 @@ package http_test import ( - "context" "testing" . "github.com/xtls/xray-core/common/protocol/http" @@ -89,7 +88,7 @@ first_name=John&last_name=Doe&action=Submit`, } for _, test := range cases { - header, err := SniffHTTP([]byte(test.input), context.TODO()) + header, err := SniffHTTP([]byte(test.input)) if test.err { if err == nil { t.Errorf("Expect error but nil, in test: %v", test) diff --git a/common/protocol/id.go b/common/protocol/id.go index 211fc578..2a1eb17a 100644 --- a/common/protocol/id.go +++ b/common/protocol/id.go @@ -1,7 +1,9 @@ package protocol import ( + "crypto/hmac" "crypto/md5" + "hash" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/uuid" @@ -11,6 +13,12 @@ const ( IDBytesLen = 16 ) +type IDHash func(key []byte) hash.Hash + +func DefaultIDHash(key []byte) hash.Hash { + return hmac.New(md5.New, key) +} + // The ID of en entity, in the form of a UUID. type ID struct { uuid uuid.UUID @@ -47,3 +55,28 @@ func NewID(uuid uuid.UUID) *ID { md5hash.Sum(id.cmdKey[:0]) return id } + +func nextID(u *uuid.UUID) uuid.UUID { + md5hash := md5.New() + common.Must2(md5hash.Write(u.Bytes())) + common.Must2(md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81"))) + var newid uuid.UUID + for { + md5hash.Sum(newid[:0]) + if !newid.Equals(u) { + return newid + } + common.Must2(md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2"))) + } +} + +func NewAlterIDs(primary *ID, alterIDCount uint16) []*ID { + alterIDs := make([]*ID, alterIDCount) + prevID := primary.UUID() + for idx := range alterIDs { + newid := nextID(&prevID) + alterIDs[idx] = NewID(newid) + prevID = newid + } + return alterIDs +} diff --git a/common/protocol/protocol.go b/common/protocol/protocol.go index 61c963c5..bd49ba69 100644 --- a/common/protocol/protocol.go +++ b/common/protocol/protocol.go @@ -1,7 +1,3 @@ package protocol // import "github.com/xtls/xray-core/common/protocol" -import ( - "errors" -) - -var ErrProtoNeedMoreData = errors.New("protocol matches, but need more data to complete sniffing") +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/common/protocol/quic/sniff.go b/common/protocol/quic/sniff.go index 0691bad6..71c14428 100644 --- a/common/protocol/quic/sniff.go +++ b/common/protocol/quic/sniff.go @@ -11,7 +11,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/protocol" ptls "github.com/xtls/xray-core/common/protocol/tls" "golang.org/x/crypto/hkdf" ) @@ -47,223 +46,146 @@ var ( ) func SniffQUIC(b []byte) (*SniffHeader, error) { - if len(b) == 0 { - return nil, common.ErrNoClue + buffer := buf.FromBytes(b) + typeByte, err := buffer.ReadByte() + if err != nil { + return nil, errNotQuic + } + isLongHeader := typeByte&0x80 > 0 + if !isLongHeader || typeByte&0x40 == 0 { + return nil, errNotQuicInitial + } + + vb, err := buffer.ReadBytes(4) + if err != nil { + return nil, errNotQuic + } + + versionNumber := binary.BigEndian.Uint32(vb) + + if versionNumber != 0 && typeByte&0x40 == 0 { + return nil, errNotQuic + } else if versionNumber != versionDraft29 && versionNumber != version1 { + return nil, errNotQuic + } + + if (typeByte&0x30)>>4 != 0x0 { + return nil, errNotQuicInitial + } + + var destConnID []byte + if l, err := buffer.ReadByte(); err != nil { + return nil, errNotQuic + } else if destConnID, err = buffer.ReadBytes(int32(l)); err != nil { + return nil, errNotQuic + } + + if l, err := buffer.ReadByte(); err != nil { + return nil, errNotQuic + } else if common.Error2(buffer.ReadBytes(int32(l))) != nil { + return nil, errNotQuic + } + + tokenLen, err := quicvarint.Read(buffer) + if err != nil || tokenLen > uint64(len(b)) { + return nil, errNotQuic + } + + if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil { + return nil, errNotQuic + } + + packetLen, err := quicvarint.Read(buffer) + if err != nil { + return nil, errNotQuic + } + + hdrLen := len(b) - int(buffer.Len()) + + origPNBytes := make([]byte, 4) + copy(origPNBytes, b[hdrLen:hdrLen+4]) + + var salt []byte + if versionNumber == version1 { + salt = quicSalt + } else { + salt = quicSaltOld + } + initialSecret := hkdf.Extract(crypto.SHA256.New, destConnID, salt) + secret := hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size()) + hpKey := hkdfExpandLabel(initialSuite.Hash, secret, []byte{}, "quic hp", initialSuite.KeyLen) + block, err := aes.NewCipher(hpKey) + if err != nil { + return nil, err } - // Crypto data separated across packets - cryptoLen := int32(0) - cryptoDataBuf := buf.NewWithSize(32767) - defer cryptoDataBuf.Release() cache := buf.New() defer cache.Release() - // Parse QUIC packets - for len(b) > 0 { - buffer := buf.FromBytes(b) - typeByte, err := buffer.ReadByte() - if err != nil { - return nil, errNotQuic - } - - isLongHeader := typeByte&0x80 > 0 - if !isLongHeader || typeByte&0x40 == 0 { - return nil, errNotQuicInitial - } - - vb, err := buffer.ReadBytes(4) - if err != nil { - return nil, errNotQuic - } - - versionNumber := binary.BigEndian.Uint32(vb) - if versionNumber != 0 && typeByte&0x40 == 0 { - return nil, errNotQuic - } else if versionNumber != versionDraft29 && versionNumber != version1 { - return nil, errNotQuic - } - - packetType := (typeByte & 0x30) >> 4 - isQuicInitial := packetType == 0x0 - - var destConnID []byte - if l, err := buffer.ReadByte(); err != nil { - return nil, errNotQuic - } else if destConnID, err = buffer.ReadBytes(int32(l)); err != nil { - return nil, errNotQuic - } - - if l, err := buffer.ReadByte(); err != nil { - return nil, errNotQuic - } else if common.Error2(buffer.ReadBytes(int32(l))) != nil { - return nil, errNotQuic - } - - if isQuicInitial { // Only initial packets have token, see https://datatracker.ietf.org/doc/html/rfc9000#section-17.2.2 - tokenLen, err := quicvarint.Read(buffer) - if err != nil || tokenLen > uint64(len(b)) { - return nil, errNotQuic - } - - if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil { - return nil, errNotQuic - } - } - - packetLen, err := quicvarint.Read(buffer) - if err != nil { - return nil, errNotQuic - } - // packetLen is impossible to be shorter than this - if packetLen < 4 { - return nil, errNotQuic - } - - hdrLen := len(b) - int(buffer.Len()) - if len(b) < hdrLen+int(packetLen) { - return nil, common.ErrNoClue // Not enough data to read as a QUIC packet. QUIC is UDP-based, so this is unlikely to happen. - } - - restPayload := b[hdrLen+int(packetLen):] - if !isQuicInitial { // Skip this packet if it's not initial packet - b = restPayload - continue - } - - var salt []byte - if versionNumber == version1 { - salt = quicSalt - } else { - salt = quicSaltOld - } - initialSecret := hkdf.Extract(crypto.SHA256.New, destConnID, salt) - secret := hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size()) - hpKey := hkdfExpandLabel(initialSuite.Hash, secret, []byte{}, "quic hp", initialSuite.KeyLen) - block, err := aes.NewCipher(hpKey) - if err != nil { - return nil, err - } - - cache.Clear() - mask := cache.Extend(int32(block.BlockSize())) - block.Encrypt(mask, b[hdrLen+4:hdrLen+4+len(mask)]) - b[0] ^= mask[0] & 0xf - packetNumberLength := int(b[0]&0x3 + 1) - for i := range packetNumberLength { - b[hdrLen+i] ^= mask[i+1] - } - - key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16) - iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12) - cipher := AEADAESGCMTLS13(key, iv) - - nonce := cache.Extend(int32(cipher.NonceSize())) - _, err = buffer.Read(nonce[len(nonce)-packetNumberLength:]) - if err != nil { - return nil, err - } - - extHdrLen := hdrLen + packetNumberLength - data := b[extHdrLen : int(packetLen)+hdrLen] - decrypted, err := cipher.Open(b[extHdrLen:extHdrLen], nonce, data, b[:extHdrLen]) - if err != nil { - return nil, err - } - buffer = buf.FromBytes(decrypted) - for !buffer.IsEmpty() { - frameType, _ := buffer.ReadByte() - for frameType == 0x0 && !buffer.IsEmpty() { - frameType, _ = buffer.ReadByte() - } - switch frameType { - case 0x00: // PADDING frame - case 0x01: // PING frame - case 0x02, 0x03: // ACK frame - if _, err = quicvarint.Read(buffer); err != nil { // Field: Largest Acknowledged - return nil, io.ErrUnexpectedEOF - } - if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Delay - return nil, io.ErrUnexpectedEOF - } - ackRangeCount, err := quicvarint.Read(buffer) // Field: ACK Range Count - if err != nil { - return nil, io.ErrUnexpectedEOF - } - if _, err = quicvarint.Read(buffer); err != nil { // Field: First ACK Range - return nil, io.ErrUnexpectedEOF - } - for i := 0; i < int(ackRangeCount); i++ { // Field: ACK Range - if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> Gap - return nil, io.ErrUnexpectedEOF - } - if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> ACK Range Length - return nil, io.ErrUnexpectedEOF - } - } - if frameType == 0x03 { - if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT0 Count - return nil, io.ErrUnexpectedEOF - } - if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT1 Count - return nil, io.ErrUnexpectedEOF - } - if _, err = quicvarint.Read(buffer); err != nil { //nolint:misspell // Field: ECN Counts -> ECT-CE Count - return nil, io.ErrUnexpectedEOF - } - } - case 0x06: // CRYPTO frame, we will use this frame - offset, err := quicvarint.Read(buffer) // Field: Offset - if err != nil { - return nil, io.ErrUnexpectedEOF - } - length, err := quicvarint.Read(buffer) // Field: Length - if err != nil || length > uint64(buffer.Len()) { - return nil, io.ErrUnexpectedEOF - } - currentCryptoLen := int32(offset + length) - if cryptoLen < currentCryptoLen { - if cryptoDataBuf.Cap() < currentCryptoLen { - return nil, io.ErrShortBuffer - } - cryptoDataBuf.Extend(currentCryptoLen - cryptoLen) - cryptoLen = currentCryptoLen - } - if _, err := buffer.Read(cryptoDataBuf.BytesRange(int32(offset), currentCryptoLen)); err != nil { // Field: Crypto Data - return nil, io.ErrUnexpectedEOF - } - case 0x1c: // CONNECTION_CLOSE frame, only 0x1c is permitted in initial packet - if _, err = quicvarint.Read(buffer); err != nil { // Field: Error Code - return nil, io.ErrUnexpectedEOF - } - if _, err = quicvarint.Read(buffer); err != nil { // Field: Frame Type - return nil, io.ErrUnexpectedEOF - } - length, err := quicvarint.Read(buffer) // Field: Reason Phrase Length - if err != nil { - return nil, io.ErrUnexpectedEOF - } - if _, err := buffer.ReadBytes(int32(length)); err != nil { // Field: Reason Phrase - return nil, io.ErrUnexpectedEOF - } - default: - // Only above frame types are permitted in initial packet. - // See https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2.2-8 - return nil, errNotQuicInitial - } - } - - tlsHdr := &ptls.SniffHeader{} - err = ptls.ReadClientHello(cryptoDataBuf.BytesRange(0, cryptoLen), tlsHdr) - if err != nil { - // The crypto data may have not been fully recovered in current packets, - // So we continue to sniff rest packets. - b = restPayload - continue - } - return &SniffHeader{domain: tlsHdr.Domain()}, nil + mask := cache.Extend(int32(block.BlockSize())) + block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16]) + b[0] ^= mask[0] & 0xf + for i := range b[hdrLen : hdrLen+4] { + b[hdrLen+i] ^= mask[i+1] } - // All payload is parsed as valid QUIC packets, but we need more packets for crypto data to read client hello. - return nil, protocol.ErrProtoNeedMoreData + packetNumberLength := b[0]&0x3 + 1 + if packetNumberLength != 1 { + return nil, errNotQuicInitial + } + var packetNumber uint32 + { + n, err := buffer.ReadByte() + if err != nil { + return nil, err + } + packetNumber = uint32(n) + } + + if packetNumber != 0 { + return nil, errNotQuicInitial + } + + extHdrLen := hdrLen + int(packetNumberLength) + copy(b[extHdrLen:hdrLen+4], origPNBytes[packetNumberLength:]) + data := b[extHdrLen : int(packetLen)+hdrLen] + + key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16) + iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12) + cipher := AEADAESGCMTLS13(key, iv) + nonce := cache.Extend(int32(cipher.NonceSize())) + binary.BigEndian.PutUint64(nonce[len(nonce)-8:], uint64(packetNumber)) + decrypted, err := cipher.Open(b[extHdrLen:extHdrLen], nonce, data, b[:extHdrLen]) + if err != nil { + return nil, err + } + buffer = buf.FromBytes(decrypted) + frameType, err := buffer.ReadByte() + if err != nil { + return nil, io.ErrUnexpectedEOF + } + if frameType != 0x6 { + // not crypto frame + return &SniffHeader{domain: ""}, nil + } + if common.Error2(quicvarint.Read(buffer)) != nil { + return nil, io.ErrUnexpectedEOF + } + dataLen, err := quicvarint.Read(buffer) + if err != nil { + return nil, io.ErrUnexpectedEOF + } + if dataLen > uint64(buffer.Len()) { + return nil, io.ErrUnexpectedEOF + } + frameData, err := buffer.ReadBytes(int32(dataLen)) + common.Must(err) + tlsHdr := &ptls.SniffHeader{} + err = ptls.ReadClientHello(frameData, tlsHdr) + if err != nil { + return nil, err + } + + return &SniffHeader{domain: tlsHdr.Domain()}, nil } func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte { diff --git a/common/protocol/quic/sniff_test.go b/common/protocol/quic/sniff_test.go index 8a85ea87..cddb4c87 100644 --- a/common/protocol/quic/sniff_test.go +++ b/common/protocol/quic/sniff_test.go @@ -2,11 +2,9 @@ package quic_test import ( "encoding/hex" - "errors" "testing" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol/quic" ) @@ -18,270 +16,3 @@ func TestSniffQUIC(t *testing.T) { t.Error("failed") } } - -func TestSniffQUICComplex(t *testing.T) { - tests := []struct { - name string - hexData string - domain string - wantErr bool - needsMoreData bool - }{ - { - name: "EmptyPacket", - hexData: "0000000000000000000000000000000000000000000000000000000000000000", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "NTP Packet Client", - hexData: "23000000000000000000000000000000000000000000000000000000000000000000000000000000acb84a797d4044c9", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "NTP Packet Server", - hexData: "240106ec000000000000000e47505373ea4dcaef2f4b4c31acb84a797d4044c9eb58b8693dd70c27eb58b8693dd7dde2", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "DNS Packet Client", - hexData: "4500004a8e2d40003f1146392a2a2d03080808081eea00350036a8175ad4010000010000000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000010001", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "DNS Packet Client", - hexData: "4500004a667a40003f116dec2a2a2d030808080866980035003605d9b524010000010000000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000410001", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "DNS Packet Server", - hexData: "b524818000010006000100000675706461746504636f64650c76697375616c73747564696f03636f6d0000410001c00c00050001000000ec00301e7673636f64652d7570646174652d67366763623667676474686b63746439037a303107617a7572656664036e657400c03a000500010000000b002311737461722d617a75726566642d70726f640e747261666669636d616e61676572c065c076000500010000003c002c0473686564086475616c2d6c6f770b732d706172742d3030313706742d3030303908742d6d7365646765c065c0a5000500010000006c001411617a75726566642d742d66622d70726f64c088c0dd000500010000003c0026046475616c0b732d706172742d3030313706742d303030390b66622d742d6d7365646765c065c0fd00050001000000300002c102c1150006000100000030002d036e7331c115066d736e687374096d6963726f736f6674c0257848b78d00000708000003840024ea000000003c", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "DNS Packet Server", - hexData: "5ad4818000010007000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000010001c00c000500010000008400301e7673636f64652d7570646174652d67366763623667676474686b63746439037a303107617a7572656664036e657400c03a000500010000001e002311737461722d617a75726566642d70726f640e747261666669636d616e61676572c065c076000500010000003c002c0473686564086475616c2d6c6f770b732d706172742d3030313706742d3030303908742d6d7365646765c065c0a50005000100000010001411617a75726566642d742d66622d70726f64c088c0dd000500010000003c0026046475616c0b732d706172742d3030313706742d303030390b66622d742d6d7365646765c065c0fd00050001000000100002c102c102000100010000001000040d6bfd2d", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "QUIC, NonHandshake Packet", - hexData: "548439ba3a0cffd27dabe08ebf9e603dd4801781e133b1a0276d29a047c3b8856adcced0067c4b11a08985bf93c05863305bd4b43ee9168cd5fdae0c392ff74ae06ce13e8d97dabec81ee927a844fa840f781edf9deb22f3162bf77009b3f5800c5e45539ac104368e7df8ba", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "QUIC, NonHandshake Packet", - hexData: "53f4144825dab3ba251b83d0089e910210bec1a6507cca92ad9ff539cc21f6c75e3551ca44003d9a", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "QUIC, NonHandshake Packet", - hexData: "528dc5524c03e7517949422cc3f6ffbfff74b2ec30a87654a71a", - domain: "", - wantErr: true, - needsMoreData: false, - }, - { - name: "QUIC Chromebook Handshake[1]; packet 1", - hexData: "cb00000001088ca3be26059ca269000044d088950f316207d551c91c88d791557c440a19184322536d2c900034358c1b3964f2d2935337b8d044d35bf62b4eea9ceaac64121aa634c7cd28630722d169fa0f215b940d47d7996ca56f0d463dbf97a4a1b5818c5297a26fe58f5553dfb513ad589750a61682f229996555c7121c8bf48b06b68ab06427b01af485d832f9894099a20d3baadcff7b1cf07e2c059d3e7ba88d4ad35ef0ffea1fdc6ac3db271dfcca892a41ab25284936225c9bc593ce242b11b8abed4a8df902987eef0c6d90669e3606f47dd6ad05f44ba3a0cd356854261bbb1e2d8f6b83cc57cfa57eda3e5d7181b6ec418f6eeca81c259a33e4b0a913de720f2f8782764766ac9602a7f52a1082ec3da30dbefcf38c781a3e033810c4f2babf9b72adf7164159d98142181492e4468c0e10ab29013bf238e7360e09767ca49d59a9eb18f06a372bad711fefa90295f8e0839b1080570648212b321e5bd6f614bf0d3dc2817628b0c052a32820c16cb7f531c49244c48eb1429625246f9c164ae4ee1e83eaa8ff0eef1acf5a3d8ca88f1e4597db5ba5c0cb23d6100dd53da4f439ae64c4d3d43d1fbb5677f4fdc3bd2c2948dfc7e0be1a33c842033da15529cfd3cae00da68343d835db867f746854804410ba68f0dd7711b0fe55817b83f6ce1a12ad38acf2a3156f819f0dc68ea799c05583d9728f2856577811b260dba40d6c5e82c9e558c5b8f3f4599caf05ea591118e0b80ad621e0a76e4926047593a896752cb168420cb1b02d4211de5e5b7c891f319b5c0cf687e1d261a01f2acbade6bd73cd1ade0a02e240e9351384e1a6868c21a4878f39f0fa94ee1e36c5a46449241a3fe0147ff50176787eca7f3a936c901aeef56770bff74feecb985e6670d20dfd8ed17952dca5a5292213345c61db09bb5bcf5bf74565f61f9dccab51a289c3160ffe4a9b29cc76ea46778d9317a890efea2ad905f4219463a3baca3c02f5c3682634be7c2e86e366272a8263fec8e871644a79299d4aa74f1b1414b2f963cce6e059978faf813625af7869c1dec92035478c0e46dc66d938d4131aca27a59b2103b8cefa8e08aeb44b53b205b932902aea8d519faaaa12e354a6f532b4f716d7929e655dc2e98b494a99153854af5732a2659f2c21e4069896a1835ad05c5e53781cab16599cf4af47c196deeff9115c80d13f93aeb28b08023e6a1d3cf7da2a4457a9e443176bcdfef8f8de630c02bd0efdc5ddda56ad8f6b47edbda6353205e6e655f690092a48deb7f8a5254a7d778e07216cd97dfefcf740c1acd2977ef0fa17f798ea9752bae46e3aa3ec9b13f4c95c20a7839b8409000fa1f17e8dc46cc05c41bff696ee03c0371cae8638e8018ff4ebedd9f27d56443e534a72dd3d18a64790b676ddd060376759fa4a12ffc17f4be83492126ec1dc0fcd4aefef73a0b9c443ec3532b9a66b1a60daacf45e6557115edc0cc4d08758754a44beffedaa0d1265e50beed1a01752904ee3f7e706ed290b1a79071b142105b7c02e692ff318710e3ce9c3b9ec557cdecef173796417341ada414faa06b52adf645db454b56468ccf0da50a942ebc09487797cb45a085ec1e2e06fcd1f5b72eac291955a62e5aa379a374aea3a0dec3e4e0ba1dde350a94c72dbea7505922e26e99d62f751c2b301413a73fb6b20a36052151473ebecd04d0a771ec326957bc28c2020fdf6f01d9abed69b3c3e73168b404a1748b15310b167396da01c7d", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[1]; packet 1 - 2", - hexData: "cb00000001088ca3be26059ca269000044d088950f316207d551c91c88d791557c440a19184322536d2c900034358c1b3964f2d2935337b8d044d35bf62b4eea9ceaac64121aa634c7cd28630722d169fa0f215b940d47d7996ca56f0d463dbf97a4a1b5818c5297a26fe58f5553dfb513ad589750a61682f229996555c7121c8bf48b06b68ab06427b01af485d832f9894099a20d3baadcff7b1cf07e2c059d3e7ba88d4ad35ef0ffea1fdc6ac3db271dfcca892a41ab25284936225c9bc593ce242b11b8abed4a8df902987eef0c6d90669e3606f47dd6ad05f44ba3a0cd356854261bbb1e2d8f6b83cc57cfa57eda3e5d7181b6ec418f6eeca81c259a33e4b0a913de720f2f8782764766ac9602a7f52a1082ec3da30dbefcf38c781a3e033810c4f2babf9b72adf7164159d98142181492e4468c0e10ab29013bf238e7360e09767ca49d59a9eb18f06a372bad711fefa90295f8e0839b1080570648212b321e5bd6f614bf0d3dc2817628b0c052a32820c16cb7f531c49244c48eb1429625246f9c164ae4ee1e83eaa8ff0eef1acf5a3d8ca88f1e4597db5ba5c0cb23d6100dd53da4f439ae64c4d3d43d1fbb5677f4fdc3bd2c2948dfc7e0be1a33c842033da15529cfd3cae00da68343d835db867f746854804410ba68f0dd7711b0fe55817b83f6ce1a12ad38acf2a3156f819f0dc68ea799c05583d9728f2856577811b260dba40d6c5e82c9e558c5b8f3f4599caf05ea591118e0b80ad621e0a76e4926047593a896752cb168420cb1b02d4211de5e5b7c891f319b5c0cf687e1d261a01f2acbade6bd73cd1ade0a02e240e9351384e1a6868c21a4878f39f0fa94ee1e36c5a46449241a3fe0147ff50176787eca7f3a936c901aeef56770bff74feecb985e6670d20dfd8ed17952dca5a5292213345c61db09bb5bcf5bf74565f61f9dccab51a289c3160ffe4a9b29cc76ea46778d9317a890efea2ad905f4219463a3baca3c02f5c3682634be7c2e86e366272a8263fec8e871644a79299d4aa74f1b1414b2f963cce6e059978faf813625af7869c1dec92035478c0e46dc66d938d4131aca27a59b2103b8cefa8e08aeb44b53b205b932902aea8d519faaaa12e354a6f532b4f716d7929e655dc2e98b494a99153854af5732a2659f2c21e4069896a1835ad05c5e53781cab16599cf4af47c196deeff9115c80d13f93aeb28b08023e6a1d3cf7da2a4457a9e443176bcdfef8f8de630c02bd0efdc5ddda56ad8f6b47edbda6353205e6e655f690092a48deb7f8a5254a7d778e07216cd97dfefcf740c1acd2977ef0fa17f798ea9752bae46e3aa3ec9b13f4c95c20a7839b8409000fa1f17e8dc46cc05c41bff696ee03c0371cae8638e8018ff4ebedd9f27d56443e534a72dd3d18a64790b676ddd060376759fa4a12ffc17f4be83492126ec1dc0fcd4aefef73a0b9c443ec3532b9a66b1a60daacf45e6557115edc0cc4d08758754a44beffedaa0d1265e50beed1a01752904ee3f7e706ed290b1a79071b142105b7c02e692ff318710e3ce9c3b9ec557cdecef173796417341ada414faa06b52adf645db454b56468ccf0da50a942ebc09487797cb45a085ec1e2e06fcd1f5b72eac291955a62e5aa379a374aea3a0dec3e4e0ba1dde350a94c72dbea7505922e26e99d62f751c2b301413a73fb6b20a36052151473ebecd04d0a771ec326957bc28c2020fdf6f01d9abed69b3c3e73168b404a1748b15310b167396da01c7dc700000001088ca3be26059ca269000044d00a7e7a252620d0fdfb63c0c193d6a9fe6a36aa9ce1b29dfa5f11f2567850b88384a2cc682eca2e292749365b833e5f7540019cd4f3143ed078aec07990b0d6ece18310403e73e1fe2975a8f9cb05796fa6196faaba3ee12a22b63a28a624cf4f7bedd44de000dc5ea698c65664df995b7d5fade0aab1cf0ecc5afd5ecb8fb80deecae3a8c97c20171f00ac3b5dc9a9027ca9c25571c72bb32070f6e3fb583560b0da6041b72e0a9601b8ad17d3c45e9dcc059f9f4758e8c35a839a9f6f4c501cb64e32e886fc733bc51069fbe4406f04d908285974c387d5b3e5f0f674941d05993bf8bda0d5ffd8c4fb528e150ff4bf37e38bd9c6346816fe360d4a206da81e815c1f7905184b6146b33427c6e38f1179981c18b82a3544442dd997c182d956037ae8f106eaf67ba133e7f15f1550b257d431f01ba0472659c6a5c2e6ff5e4ce9e692f4ef9fb169a75df4eb13f0b20e1994f3f8687bdca300c7e749af7b7a3b6597a6b950fe378a68c77766fdabe95248ed41d37805756b7ffa9cee0898bd661f6657cbf1af9aa8c7e437d432ca854c95307e6a7dfb6504ee3f7852fb3c246d168a03810b6c3d4e3d40bdee3def579effb66563f5bac98cfa1b071cd6f33e425e016bb3514a183b72cb3a393e9e519ba60e2177c98f530835e3b6eab78cdcb8abdbc769bc07e10c8e38bea710d5de1bdb2fa8d0d9b19e8cc31d16725a696e55342c89b667497e3d7f90e48f8503d8ead2a32a1930c3b24a4a9dcf2d8ec781705dd97d7df6e26828712fe42114419d5b8346bd86c239bd02f34e55f71400cb10c1fac7d8efa1a2ab258c17ace4288c8576ab92447b648fd15f4e038ec1c81a135e3bbb6f581a994c6a4902aeb1b5588cb1b5b53c8540296d96b6d2eccd67bae9609233f36304b5186d4698b88bb3ce8b1191a62b990436cf10718fd5759cb2281ac122f49ccbef8a3206348c1a930e7fc4bb498a11d89374e1480c7b8725b5f65e8c8d6f58da17f9134abce77eb9a6fcda514e7d3ab2e3610f86945f0dca519a3844da1b3a4b0e03c80528a2f79be478d07ff26166e30294bf0e69bf07a5bbd6d879adf6d618a1ec8365023408980bf67f0525a2fdee97fccc38fe104d4f58ed15e3671dfedf684856a27fbe286adba40ff0336def93f0174e9e35d341f5de73190d330d72227db9a866b69418e17e8e19ec884c1ffe2f0ad6deec37c9d49d536d0242fab282b0cf86cc9b15341757e0d361bddcbe5cbb062b3148d7c3c62af5c5dd5922a49920f351647030f62ed16929a404aa514fcbc38e67ba4f275e02a04c486b1a8e5b5efda197fd63e6f41fdeffa652c690dd6b00ca65df3688672ead9744f7d631e42e3b42f3ed1bff51b30f89211a7467cde65eab3659af7690cf307420a5823f31999d8f63c6c6ba0296ed4a46d5df6404f8db33e7252cc6bfcf7f55fee1f1e3b0573b6c6615793ff0691b7cfd23c195f66eb333d7efb0cfb74cf159787f87ad01fc131c6763bb1117bbfb8c2e8197ffba6b8c747565b1332bdbd6553b840939c2f98aa8eb1c549491c640e012fc549852fa7a93f81e5db152c761fc7d01bce0325619965c09f6730a162e7be53af7d9ce4b5ac0f4eb487361d2ac231d4ce92e5d9a084bc7b609ccf60056ecc82cd0c06a088cfbcf7d764b3109331c42f989da82b05cfe4c134a6784e664fa67a89c0624e3cc73ccfdea3f292db28f7c7b1b109f680f6b537f135c62f764", - domain: "dns.google", - wantErr: false, - needsMoreData: false, - }, - { - name: "QUIC Chromebook Handshake[2]; packet 1", - hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaa", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[2]; packet 1-2", - hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaac40000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e4489522d29bb5c84749f83c8e1edfd9da8d1738164a8a9c59e37a5c9994d90bb982dcfa69b20f868960dc139618f1adc2546d34340ae13d826260c54a456bbf7469ee37b1be1d7177004468d7e92cac62a0b165d6a114ad479861dd58959e094b5a6250359301d4a614d529660760e3d1cdec9bf444a3761309bab40e4a977bc749e0dae431952f5f7e6b1ebc1383d343359a387da4301f7fa4b400475e9b82367e56278376dd1c80349f083988945a13649008109cc12a3acf569ffcc5481fbcd86b544e7dc8434e9dd42bd8e5716a844d37879568db046857389d36cc7550c75f94e314db6749aa987f0fc730fae0fcf465d01c2fb745269dfc10132ddb5404dda2f9455780f5818730834aa9db4740793359884b9927b0bd1a5ca96052b4f17397d8b78aa891401bb8bed6726ea2229d919798c50e24d5f40576ac204847be9244aadbe5c773684c37475036541d209c177d4e9c22a1253292ce4ffb886b925b6cf83cc251976a68887eda2777590f51804b790b51eee77e717b7ef0eea71634594df36e6ae9e7574d65c51ac3196f0b2a3b0f023c81f05f7807f958dda03418ed49e14b645e814b9aa55b37c809be3172ca21fe4c7a78e17e9ece8def2dd2949310ecaa41b1b477f4e85db5288aa144e333f47ef291d0e822941181c13859d9fd6d640904ee764c9276125228c932dff3fb12f564f039b52f5ba1ab4d119641df8fe13f784802b99347f0046da63f471e34b1d12d3111cffe7b5d90cf5999879f6f23e7785f09cb10df32821bb68dd8fdcfcdbedd63f2428b2292b9f0e76ff36403c9644fd43e01112ee6218d0ec1c86f6d147e4b802293e906750c7046f53bf05a144e321d3b45e08e4064fd3828fdd1b5d1ceed74081f61319dc0ad9a6e8a3b9cc802e952d24e2271712e2c2cda7daca2f835e6c804feeef8d918404cc82a1aa9534bddff68a472b208a0d0a7fd68a08fbc411132af47a6b67a32617b7b9991524c21599e8e3cb9395cdab87a3f5bf5d1833a9c7ea021b29cf428c877c6b21d62f99340ac7f85ae721acc10968e7d79f111ca40c75e14060d07cffa046d71151a0b00eab657300344b04bd1a8871650c34ceda8610d7c1ba8d37673da6aaa580400e0230c69fba8ba21927de2f5897656144694550d1df3d268804adc707e7b236501734aeabb2e61cb08012bd96eca5a486d7a55f996992c36233815abd71c30e263ba0c5d9456fe0828df16f6af7929390bb143c426d9dfeaf4bb373554479ebe609b36b4bc3dd08ce216b9cdc5726edb458c5e4036d0edc688d3e39d20f8254b5d1f174518f15b344efc27fc56572c0159aa593d5b46bc33818f986e3df8caebd4c7b702ef50dd582714a2b94ecd1c4e90af37d388445c478a32ff6e8f5852ee115966b708eed04da322b98813a69423e95f90b89ce85518e39bcef36fdd5bd312b2c6c5ee85962675274c18f39ee35155517f70fd74b31bb2de6b5108d369252e6fb289e453833132ef7960da1cc0934790c039b9a1b0c74f23eb3b61fe9b4d0ea67de757b93af451eef303b1373199af446a0fa98d5991bbd4771ee63317e6da86efbe213dfff595c41b98e0e89f4f2df110104e760feebf4cb3361171c9fceb1e1c809a268", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[2]; packet 1-3", - hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaac40000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e4489522d29bb5c84749f83c8e1edfd9da8d1738164a8a9c59e37a5c9994d90bb982dcfa69b20f868960dc139618f1adc2546d34340ae13d826260c54a456bbf7469ee37b1be1d7177004468d7e92cac62a0b165d6a114ad479861dd58959e094b5a6250359301d4a614d529660760e3d1cdec9bf444a3761309bab40e4a977bc749e0dae431952f5f7e6b1ebc1383d343359a387da4301f7fa4b400475e9b82367e56278376dd1c80349f083988945a13649008109cc12a3acf569ffcc5481fbcd86b544e7dc8434e9dd42bd8e5716a844d37879568db046857389d36cc7550c75f94e314db6749aa987f0fc730fae0fcf465d01c2fb745269dfc10132ddb5404dda2f9455780f5818730834aa9db4740793359884b9927b0bd1a5ca96052b4f17397d8b78aa891401bb8bed6726ea2229d919798c50e24d5f40576ac204847be9244aadbe5c773684c37475036541d209c177d4e9c22a1253292ce4ffb886b925b6cf83cc251976a68887eda2777590f51804b790b51eee77e717b7ef0eea71634594df36e6ae9e7574d65c51ac3196f0b2a3b0f023c81f05f7807f958dda03418ed49e14b645e814b9aa55b37c809be3172ca21fe4c7a78e17e9ece8def2dd2949310ecaa41b1b477f4e85db5288aa144e333f47ef291d0e822941181c13859d9fd6d640904ee764c9276125228c932dff3fb12f564f039b52f5ba1ab4d119641df8fe13f784802b99347f0046da63f471e34b1d12d3111cffe7b5d90cf5999879f6f23e7785f09cb10df32821bb68dd8fdcfcdbedd63f2428b2292b9f0e76ff36403c9644fd43e01112ee6218d0ec1c86f6d147e4b802293e906750c7046f53bf05a144e321d3b45e08e4064fd3828fdd1b5d1ceed74081f61319dc0ad9a6e8a3b9cc802e952d24e2271712e2c2cda7daca2f835e6c804feeef8d918404cc82a1aa9534bddff68a472b208a0d0a7fd68a08fbc411132af47a6b67a32617b7b9991524c21599e8e3cb9395cdab87a3f5bf5d1833a9c7ea021b29cf428c877c6b21d62f99340ac7f85ae721acc10968e7d79f111ca40c75e14060d07cffa046d71151a0b00eab657300344b04bd1a8871650c34ceda8610d7c1ba8d37673da6aaa580400e0230c69fba8ba21927de2f5897656144694550d1df3d268804adc707e7b236501734aeabb2e61cb08012bd96eca5a486d7a55f996992c36233815abd71c30e263ba0c5d9456fe0828df16f6af7929390bb143c426d9dfeaf4bb373554479ebe609b36b4bc3dd08ce216b9cdc5726edb458c5e4036d0edc688d3e39d20f8254b5d1f174518f15b344efc27fc56572c0159aa593d5b46bc33818f986e3df8caebd4c7b702ef50dd582714a2b94ecd1c4e90af37d388445c478a32ff6e8f5852ee115966b708eed04da322b98813a69423e95f90b89ce85518e39bcef36fdd5bd312b2c6c5ee85962675274c18f39ee35155517f70fd74b31bb2de6b5108d369252e6fb289e453833132ef7960da1cc0934790c039b9a1b0c74f23eb3b61fe9b4d0ea67de757b93af451eef303b1373199af446a0fa98d5991bbd4771ee63317e6da86efbe213dfff595c41b98e0e89f4f2df110104e760feebf4cb3361171c9fceb1e1c809a268c60000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e44892ff5e6b16d8a259a9128c2c0c3c525462781a344c3df7f19a747e0e79ca8714995c867fc697a3cb87b35e769465a8e966bcb35b7e897ad036aa23a6c021e2445a0eb79962151cd20dbb43ae1231847de01caf4e5589dfebf026e95f7d1d742e140d9dda849396a70cc0798f1eef06fd5f4cfbc9a190ddf04cc332c5b7b15e53af311190ced92a1291c12b8799f2b50e076539a8370ee667e1791a78f38e565a48acbaa1c78ba941dba8b0d040f8fb8bbcc9f6bf5705efa613a24b12d6ac9cebb4f3fac1b09a07b49d8a3a62808eb0a324629f13a012e6ad0feb11ad97c1572983c713b62f27584809ba43e64e4af9845af807c0783104838f4e2ac33fa848866f3cc64a7b6203a5c09e8ad231f0f06ae2fb7b39a64cedd823b0ff297ad9be1ccac436777ccb3e22ef6b9c12e6d5e34926f50e8ca8c8c0532c810b074d001c11791a01bf25786b57a5da54065dcee4962822e929f47ee44d3b8c83d45a8b7a936dc2a6fa396e4194fa032d1627eca59f69857fc40dab5835d3613dade1c74b09c345bd32c509e9545d2330b157a7acb76409f3ac8eaa22802414f38c5422fe4c5189caaf5c1b93ce7c0892f0cfc477490d335aa78961d632a973cf106bd974c2714176fb0f98cf12f2887a0d7bd491756dd374331eb3e6adb9f2bd0d6b273403fd14b314eb27ebbb6f6e78ce310437004b757c048149cf04429ae4a6d6e65c9b3e0b9c9c4d4ef52007eaaad9670320f10cd5317b3d3edc374d45c98b217dd28fb3c2c2fb6e74a3aced143e3242084b192ba6df24e69fdb883e850714fe27a45f43883486a986574fd1fc10f259fe90786441554514c8dade1f3b86fdaf5f54ab655e2d803c98aa56073b00c32148a1ed367dff3a2bd934ecba55141389990b661bbd9ce1ef1def13747d45500daf92cec9e60908274703e761cd46affd46622f2a2192a79425ebf51c875fc7ba3598e15e0ba2465fc3e87c8a5da1915d3b8abe4b16d21259f311183eee1e7d2b808a91a7c89b284df0eb6a2a79c610bbe47722b3e04d5a6c0e574816a94d97349b6976010eb8c7debf42210982f78de482b7cd068051bf57908dbf46b5ceaf64f5fb33ede4412c1ce81eb1dfb4e99e10dd9b57ebc6e62ecbf4ee2db04d9e48c62bd45f8fa51704d414296a2d51d25ced6a192034a44c67e09d8985b573f98e03fa36dd8dcce2c04b4d5b1f276b6a642aadbdcafcf09de1d234bf8bbbf64aeadf01519ddafb419b3e62d204e04c3d7ebaf54b09e387ac3e9c4781c11625a2f44fddb7a1886f21929bd01c283f64903b6ccbb463984dfadc00f6af2a421517da023fd319f528195cac5fe907624b70c0172479d07d78e266dbf20ab8fc302228f279ffdba7395a839c4a9d7a4e001a260e1702393968f1e9722f023b204cf09cfee9a7bba045e4a2a449ee9fbb5c36e93028cfc87a2e34914b1b4f01beeded175ac0fa73fea9292f2bc3b1247164d8e05cddc3981bdc24e5f596571c418f6fa00fd9d4d0898cbf0d2f5413bed5f100f1854903017b6bc88bd7e303b5e0e2417bbcc984731128eda550d31f9af0e6e743eb6916466bbd435617d56fa60b05cd7dca66a9f6f4be23d3c5ff5d900822c6d1d8d71b0bab24f57d9682381a87c", - domain: "signaler-pa.clients6.google.com", - wantErr: false, - needsMoreData: false, - }, - { - name: "QUIC Chromebook Handshake[3]; packet 1", - hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948f", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[3]; packet 1-2", - hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948fc90000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd448983eee52163a177650f57b2cd8404bc619b9b59e796f9808bcd549ae6ae30d448c90f2783978bf9314a8038f45c0da5983163bd26f38f559f59447e8cf004f93b6b5c8af7b09603db021d4bdfa641bd83926eae1709a7a427add14df90cb258c6d4663d4d29709da89c90613d2ff9334637d53ca89407804eb863f78e110b866af2734c980705d9f969730a41132e788fc9e426d0f68ed24157aaff0383438d2715262e9b8b03cff850ba88127a05a8b68ac9a5ae5b098bb9ba5eaadd71ae846b3c0f68db728361eb8c8ed899c77725afbdfabf93812c49cbf4ee64047a96ea71258dbe5be3f988029d005fa2d9fc6e1e53fbeb6888074521b972e2ac71b4f22b754fc743e0de21af1e2ab416b2481e03227a1c7d7ea6cac5bc37ee3597d3bf11bf13a688dfa3d9aeff1eb1a7fdfcf8b6c722a4853f7c2b2d31e0b2b691f4273d4793fbb7a00f27a25577bfcba95e60699c9d2a926e71d64f535b633f2fd03320b28fe86c6619c54b34e6caf8f5a71b8a144c9236bf07edaacb486ff8ac63173af099efe7c9d006a5bb756449fb32b1fbff2e315fd5e96b586bb922a9795e29ffe6ead037c556e1bf30e24afb344cf873201007096b6f687f157588e236b71ade4d9245d8f065f2e23b36fad798d0f5504ddf25b828698d0cbdc28478b20d692d2ab605797a67232b0795927d886de798f00b4e7c69517d62b748e62e01d53dd1e77ac9a1605c0408713ff309ad53ff8f2bef17f9074f01134374068bf1f5dc07125180b5ea6902ec2d55c7d6d5f7ed4ef8732f9d34b4627678611fc9579e4321cea012c4e457dee6a11c41bdd1eb965056e885757af389079a558434eb3d59ae56a232302759431172ecc88de1c5400265f0f47e21396e3c38e0ba022c3e55ee4b85527cf49dece94445adc740cd26c18004a1cb984cc1732a138844da1ab003f89c589b6f3cc10c99a1b0d87be763f83e1b12c6fa6938ebc55d2ba33c25ca816dde207f7186f0c70b56b33feb538eb31175fdcfb036e365087f1b630628affdbbdee20d1976cb009f32db5f35aadb8117aa02ff2da9bdeaffbcc8bf3412efefeb00365e5f1ea577afd6e1c3585c67ffe1fa120382aa54028dcae9bbd624432a6256687d05483f2611f1ddd14b40f66fdf547e7eba904a79bd27733c9a8fbfb01154dda3457c4eacff8116941777ec570ff040e217d648ea5076588a6417462481eba68ebc59af04ba49b92f70b68a007977fde48b94b0af35475ea19cbec92df6449b065880bf03452cb3b3582f3d1a010e585be6506f3e067226471a94ce46c515f20502b3866553c10f037d9be89ad5858d6b2d2d94c70159247f66958d0e841d1c5b4254809d52475fdf96d087c3c6647b86006147a9ebb3f52ea6f4b89d886725b9e9243efd95e434bd8dd785143c57c06863b68df8f832987eb0c730c8b96634c1f888da2ef420cb0ebacf81f4b25c65962ae40c09ac4b0b2d440e3bdaa7309d87a1fa6af1c2e13e7a63c253fae027ceb2067cef8421b62d205f5d37c7204eaf594b1b43f9d9b67509a6709df48769ab9e1078f9e59d7656ec2132b5ebccf297e757a052835fffe94ae073131ac49c4f4374a1904cd4bf3041b236b73ea19eaa583db577fe35", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[3]; packet 1-3", - hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948fc90000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd448983eee52163a177650f57b2cd8404bc619b9b59e796f9808bcd549ae6ae30d448c90f2783978bf9314a8038f45c0da5983163bd26f38f559f59447e8cf004f93b6b5c8af7b09603db021d4bdfa641bd83926eae1709a7a427add14df90cb258c6d4663d4d29709da89c90613d2ff9334637d53ca89407804eb863f78e110b866af2734c980705d9f969730a41132e788fc9e426d0f68ed24157aaff0383438d2715262e9b8b03cff850ba88127a05a8b68ac9a5ae5b098bb9ba5eaadd71ae846b3c0f68db728361eb8c8ed899c77725afbdfabf93812c49cbf4ee64047a96ea71258dbe5be3f988029d005fa2d9fc6e1e53fbeb6888074521b972e2ac71b4f22b754fc743e0de21af1e2ab416b2481e03227a1c7d7ea6cac5bc37ee3597d3bf11bf13a688dfa3d9aeff1eb1a7fdfcf8b6c722a4853f7c2b2d31e0b2b691f4273d4793fbb7a00f27a25577bfcba95e60699c9d2a926e71d64f535b633f2fd03320b28fe86c6619c54b34e6caf8f5a71b8a144c9236bf07edaacb486ff8ac63173af099efe7c9d006a5bb756449fb32b1fbff2e315fd5e96b586bb922a9795e29ffe6ead037c556e1bf30e24afb344cf873201007096b6f687f157588e236b71ade4d9245d8f065f2e23b36fad798d0f5504ddf25b828698d0cbdc28478b20d692d2ab605797a67232b0795927d886de798f00b4e7c69517d62b748e62e01d53dd1e77ac9a1605c0408713ff309ad53ff8f2bef17f9074f01134374068bf1f5dc07125180b5ea6902ec2d55c7d6d5f7ed4ef8732f9d34b4627678611fc9579e4321cea012c4e457dee6a11c41bdd1eb965056e885757af389079a558434eb3d59ae56a232302759431172ecc88de1c5400265f0f47e21396e3c38e0ba022c3e55ee4b85527cf49dece94445adc740cd26c18004a1cb984cc1732a138844da1ab003f89c589b6f3cc10c99a1b0d87be763f83e1b12c6fa6938ebc55d2ba33c25ca816dde207f7186f0c70b56b33feb538eb31175fdcfb036e365087f1b630628affdbbdee20d1976cb009f32db5f35aadb8117aa02ff2da9bdeaffbcc8bf3412efefeb00365e5f1ea577afd6e1c3585c67ffe1fa120382aa54028dcae9bbd624432a6256687d05483f2611f1ddd14b40f66fdf547e7eba904a79bd27733c9a8fbfb01154dda3457c4eacff8116941777ec570ff040e217d648ea5076588a6417462481eba68ebc59af04ba49b92f70b68a007977fde48b94b0af35475ea19cbec92df6449b065880bf03452cb3b3582f3d1a010e585be6506f3e067226471a94ce46c515f20502b3866553c10f037d9be89ad5858d6b2d2d94c70159247f66958d0e841d1c5b4254809d52475fdf96d087c3c6647b86006147a9ebb3f52ea6f4b89d886725b9e9243efd95e434bd8dd785143c57c06863b68df8f832987eb0c730c8b96634c1f888da2ef420cb0ebacf81f4b25c65962ae40c09ac4b0b2d440e3bdaa7309d87a1fa6af1c2e13e7a63c253fae027ceb2067cef8421b62d205f5d37c7204eaf594b1b43f9d9b67509a6709df48769ab9e1078f9e59d7656ec2132b5ebccf297e757a052835fffe94ae073131ac49c4f4374a1904cd4bf3041b236b73ea19eaa583db577fe35ca0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489ea77dbb530c7ba127c66c3d7bbc00c336fd4e09e1775c646dffaa8696f7b8b00bf91261fc5164d57a4b9652b7cff4e301d32224b4e48cbfca535b2070ac46181615358d87e244ba6e369f6719bd5a551ac05dc78c222fd0969d0d943cbfaa3570ec25ab2768e9679d1cd1a3528659d010c409a0719526c44e4d9915dc5b0618ebc9e35f06b31bfd8e01fad99dabe32f6bfa00b3a5db5a01920d6685c34efb958729ffc5acfe46b3605715149b65b2f638007885a0866bbdde6765992b9acce2f527de906443f8643845489f1224fd3bbbb3fa78ca4848fe0167ec7cff8a05a17eb7c7a05a80c3106647e5d9aae350f33d10f3a60ab1c705858323a8f610d98cc68ef3cea66eedbb788b9a3da873bfd44ed632aa952ed7bb2004f4502260cef0596ec6e82e7683bdd2cb1f63b01b3f928ffa86b89cbeee922f1fd192fea0bdd17cf62d14f06f9e27bf5cafec90ab26f103e1dcb96ae4335e444b1fbad294cc395c5dc3a1c0c1fb4078d362eb229c42bc42ff53115c51f137d75596b3e6d3d28974720d6935430054c6b630bade51ad508d31fdfd572bd37f70e3dc06021d2b0ccf91a7975aa501e152d62980f02ce0ee94b547a2fbede47cf1f5c0a541ccc8992dd006f77437ce6a6b1f4f91833914a1cc51acf9336a620c4a22073966cde3ecac3224941dec004e741e05c11b43796dc531ce33e7c9a4fa68fa689880842e37a3a04fb75f3fcee86813388df74d443d1c35d7adea290effa98309b22ceca9bc252ccb4c443733db691adf0af559a5b7565043f84e91c5ed9f79ebf49f0bd60b68a7b8730032574e8e21548204c75321a374bbbf822efb1281ddf32feeced4bfe22bcf7c1a309954c1e356175a8a1a1a074a22f4561acd872d813c88ea9f0f22ac8d7b7b2088bc8565e1c56dfbc84f57aa38c2600ee20e8736076a91ee73f3137e8da3fe3871587b8bdb0a08af40babbe5493f036b45eea837dc15f761d9475d27a512a2d9dfc1ccdb81e2b581f91a5d7fe67cf6955427315a4e9c158806e651e4acff40051cd8a44b0108876c82f7b4d69033bfd8216234de545bf8fb58e489bc74d366db5e48711ba7f317dcdd1708ed5de97468a6026e15bc68ab11efc90f5465b4466bf384a8cc95f9c7fff91d776cccceeae5badebc31c3516c93a7b4682212e5a4902a9fd0327234749d83c141db2eee9688a76f4361f8b6213c88ebde69ebb84488c9ab8f42737da123eaf39373ac687df65f817939296f5477f92ad3fda0effbdd5d0594eb59d80265eef6cfcaf81b386c9d03c205c1b6714bf31be15e8f871b4791aec10884938285d6b8c18a0dfe750b753de88a2d2d855b9d1a0068ac4d2ce3a259bfdd30414380bf8b287abf2a28de442552f1d70a0aeb0867d9c7ed4e9717565ebc6aca21d85d2845faacfd8fadb1a76d9a2cd619413e631666009085bc7dad7492654ec20431e37ddd55588d2cd4d256021547cac768dfe3f7dcc9a18f0c72a743de799e98398b9bb2f216aea727d240b2f52ee269f4df4b8d7bdb439f074e3ef179ae2ae44daba64864fe427574b659a5e79defaf43e45e1357e1ff48e28ba6384c559cd036c9229151f917865b5575cda11e1ee0690bfbdb628b9a17e9c37190102", - domain: "signaler-pa.clients6.google.com", - wantErr: false, - needsMoreData: false, - }, - { - name: "QUIC Chromebook Handshake[4]; packet 1", - hexData: "cc0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944894c6153ddd936992f160ae349757507f79fd0485766987d986518d9f19270a0021c52bff14e594c074f3c5664cf3de3b761cd36c16acf68565aaaeebf196da581533f19a464b22404b13b46ae3e4819bd4a7a85db6ddf379bb84f8dffcbb412c9ce405d8b4c98d303cb13df2e80a88ca0f09e2e2489c8d0b6d5ba9262b85869f8f989e9b82c4a270592894fda96bd27ce03e0cb4d4a4e130d9655e6da02f4348c949bc9b2aa609fdb4b9a0a3e45be25b18fbde569bb996d6419e98f2d9b7a0ca63f52f054b50771dc7c580596d86b4a94642be9a9b78a01ad2deae4607d8e0e25641aa8ed46b20fe027f4b9f77d1736aa0fec4f837cf5d878b3dcefdf3c6e82eb943dd022e98d623403950e6ea3addc0f93f92a422ed686b7beee437a4040f4a440dfd071d8c09f1344c28545b4488765a455e33e13d17434b9642dedadcb6a13ec35d51c3e7a03ae9a76cca6b1cba8312b7d8bae703e0a378300a17b7483d07893ddd941dd3e545a66faa0fabb4dc967c807665ebf4562fede176719d1a126f228acc0902e7235972a6db4eedd6547c38705629ee2574c5d2dd6c76c5c82e741f33291506e66a8df65ef6d1e7e6628fe4a4f5e141d482fc5f9d26609e64da8061eef5c0fca421d5334b199ca8270612074a1f9fbaad8b98ff7b81f8871f4ada6976f254e47c51e03d4c628beb3471ba375642ae0b41d65dec1419cd31f20ec779f5666717c1e7b4240fdcfa46a774234961083e1915e938ae0d41a66868b91949d856065c4e6813e0cbd9680a916b5eb78655dea9ad0f9c0ad0f5c244a72fcb8b589321519e34f0e6e25e6bb43abfa84a7241bc02555fa9060b9ef55f1e3a9dc3a575e16b23a36aabbd3ddeaf8f2516224179a4039a891e7f29631c2a08745bb184c66ffe98bc960e6c08a14524ae34444433591cdf7adc14419310b74594305f67c3087a2c21733e6e0be748e7af6fb6717946c313fbc0935ad3559e2d6323979cdc3bd48753b5a438605e15832efb8a0c4144060f41ed27a82dd067f2caaea3830abc97d9e080b3fd762aecbd58e8b2b17dae553dacdf3ee44198d2f19c0522b6a1b17923a210cf24902c5590afe808fd22e54e586399665d588a7febd0b402a4e6283679e1f95a2d4d7d3945e2bb8f44225ad8aa07cd07d3323ce94f39ae4c9466c05ceeb0a30981cea022d1bcab8a4b0c8e42e08211ee727728c74d7945f2350a149eb9cb7eb3a280954b64e612b53b19016a4c07427945345ffb86982c113ed797172ded4428d6b95b9ce64b48e98ab96421a179983c4a74b986f3f52d9a2d7fac8ea0955835d241bf4817a42950e2b298e51de20c026df81fdb0d28c68841bf62dfcfb4684def62c13ceddce9a25b446043056006ec8582aea14eee602eb2963f575dedfd2313d7d561c6ceac0d08c94645a222b25b7493542fee52c316f06f583612ab2ec3d420a01a61fe80b099386c2fe647292769d4571239592fe7e27f4324456ef894643f72ac450628cdcc9f376607b85f369a092c64d7d5a0559193e29cbc48e9ed77fa3fa05776d6169fbdbafa507db1e1d33a4550003a3a1a794b266e886f483eba76a8629d17d9596574068ffce61a52b209c21c77f5e7337e5541755c9f1c6b4", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[4]; packet 1-2", - hexData: "cc0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944894c6153ddd936992f160ae349757507f79fd0485766987d986518d9f19270a0021c52bff14e594c074f3c5664cf3de3b761cd36c16acf68565aaaeebf196da581533f19a464b22404b13b46ae3e4819bd4a7a85db6ddf379bb84f8dffcbb412c9ce405d8b4c98d303cb13df2e80a88ca0f09e2e2489c8d0b6d5ba9262b85869f8f989e9b82c4a270592894fda96bd27ce03e0cb4d4a4e130d9655e6da02f4348c949bc9b2aa609fdb4b9a0a3e45be25b18fbde569bb996d6419e98f2d9b7a0ca63f52f054b50771dc7c580596d86b4a94642be9a9b78a01ad2deae4607d8e0e25641aa8ed46b20fe027f4b9f77d1736aa0fec4f837cf5d878b3dcefdf3c6e82eb943dd022e98d623403950e6ea3addc0f93f92a422ed686b7beee437a4040f4a440dfd071d8c09f1344c28545b4488765a455e33e13d17434b9642dedadcb6a13ec35d51c3e7a03ae9a76cca6b1cba8312b7d8bae703e0a378300a17b7483d07893ddd941dd3e545a66faa0fabb4dc967c807665ebf4562fede176719d1a126f228acc0902e7235972a6db4eedd6547c38705629ee2574c5d2dd6c76c5c82e741f33291506e66a8df65ef6d1e7e6628fe4a4f5e141d482fc5f9d26609e64da8061eef5c0fca421d5334b199ca8270612074a1f9fbaad8b98ff7b81f8871f4ada6976f254e47c51e03d4c628beb3471ba375642ae0b41d65dec1419cd31f20ec779f5666717c1e7b4240fdcfa46a774234961083e1915e938ae0d41a66868b91949d856065c4e6813e0cbd9680a916b5eb78655dea9ad0f9c0ad0f5c244a72fcb8b589321519e34f0e6e25e6bb43abfa84a7241bc02555fa9060b9ef55f1e3a9dc3a575e16b23a36aabbd3ddeaf8f2516224179a4039a891e7f29631c2a08745bb184c66ffe98bc960e6c08a14524ae34444433591cdf7adc14419310b74594305f67c3087a2c21733e6e0be748e7af6fb6717946c313fbc0935ad3559e2d6323979cdc3bd48753b5a438605e15832efb8a0c4144060f41ed27a82dd067f2caaea3830abc97d9e080b3fd762aecbd58e8b2b17dae553dacdf3ee44198d2f19c0522b6a1b17923a210cf24902c5590afe808fd22e54e586399665d588a7febd0b402a4e6283679e1f95a2d4d7d3945e2bb8f44225ad8aa07cd07d3323ce94f39ae4c9466c05ceeb0a30981cea022d1bcab8a4b0c8e42e08211ee727728c74d7945f2350a149eb9cb7eb3a280954b64e612b53b19016a4c07427945345ffb86982c113ed797172ded4428d6b95b9ce64b48e98ab96421a179983c4a74b986f3f52d9a2d7fac8ea0955835d241bf4817a42950e2b298e51de20c026df81fdb0d28c68841bf62dfcfb4684def62c13ceddce9a25b446043056006ec8582aea14eee602eb2963f575dedfd2313d7d561c6ceac0d08c94645a222b25b7493542fee52c316f06f583612ab2ec3d420a01a61fe80b099386c2fe647292769d4571239592fe7e27f4324456ef894643f72ac450628cdcc9f376607b85f369a092c64d7d5a0559193e29cbc48e9ed77fa3fa05776d6169fbdbafa507db1e1d33a4550003a3a1a794b266e886f483eba76a8629d17d9596574068ffce61a52b209c21c77f5e7337e5541755c9f1c6b4cd0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944892d4ba45357f2ca515d03b90820bb91c531a4be27266fda6022856da650cfb9c34139e8a3180e93cb73a6864471f849bdfa26c03e30c0e4d00309207cd46fb48887f60d7c51b208c247d1b311b35da70dd682cb1f7ae6a64215e5fefe25249daf308083837a3898e6052ebcf6cef3cb8e987ee1eb5ea797642d76391ae363b8eb2409d7486dd4a67c9e9b755376ca61009cb853835850e4fc1844f8e9eebca73e89317003482f70c4795ce9e2724c6d62172e010233e7bf203dde6eea9976f29896df562e8640a4ed88b5b3dff50296d0db43885f162c588d72de357c2ee049d9532642576de64d4e13cce77208e0aa9cf9838166f3375a968a5a6a01cf066ea0ca27fe4471cf0bf7eb36227867928076985588d05692d3f81d9a1158d150b2701399ee0a32693aaac43c27b76c657343b2a307e7018c2e9fe6317ec09f9afb762075430140b15016ae44acffc7467f4b1cec619942e916047c2db27f89742e53856d8c7c098beaba710340674a3f8455ab38fa2a4156fa3a45dffca1e20ec86ae792988dcb52bbf2ac97ea878e80511d3e4e70ece4b2816401ad450b9d13a1fecd7a5a363dd120285a972d52c06b632362cb8f897f799fb8342850b6670eb5083347347bd48b559f118839aa627598379963ecf18c2a900399ed936ab77ebdf95bdc5eaa75a903005e38dd99362b3d99f07ee2aea1a0ada77ec5ba76a7da2a80b672f4709bd32fad36787e37467e75fe594a24b402a6fa7e858c2abe9cffd9e885cc7091c035e4354779fc113bc084b5f6fcbd7bc618e9cc15205538cf781c96b658565cd8e39d95001d085d52f87a2970eb8f72149f4061d629ca0a928442b586aef9105326e13c015ce2b987c442b574180550302c48c2cf763b45492e25adee42d23bb2608e284caea4b3a28b77a20768dee6c0002b16e5d714eca22ca0c58195e03951b9564079ffa61c81afb2c5783ea2b8c0605d3994cfaed3c33af2279469c771269174fc5879b67617f571eb376161241ca5a89332074635413661b7fc5f925d86afb56b296dedce33d2b5011fc3b85cfb769c4a1cf5d6217ba3db367ead2159a310cd398b31df83f48f722a1df6c4b6604b2e288aa57cbc9afcf42764ccbc718a3ca42895b8e8287d632d2dd47d933a7472fd7be596c4241220917e636a44a139ec16600d74104fa6055a77c34bdbe14fb1ee56b4084d1d2dc1d56f8d636a8393385fcb4916670eaf8553b2126b12c8f187f58f00ba9bdbe8f06662688b9b8c7327b2d2c2f1443c8b87930d0948c3db62c8becb5b38cc7e484be184400f8c6293568abf1714e2832ad2c0aeb88dde64686fa7b4cd8c452b7365e70221e62dd971db057d4f8eed86e6802bbe8f56f75a8fe65d8216c81265e514042dca73f20a50373fb32e9bd62b741021337a3200b5f0a1b1329e99c57c75a60850f6c2f82cbeb9001edb54d985b7d5cd5957b31f79ee77dd8e1ce5e70d50806273885886b93a12c3dc0f211383d180a475d65bb54b0c5f761f456bfc7d045acd2c7ac648f3bf27bdb52218be48cdef7aa1ddc93e3ce11067ef4819796a4e2a30459c879841789b358b9430368b0910d6e6eaa14894f36b12fe9dd2bd1a20ddb6b12ab8fcec7d0629c9a7", - domain: "play.google.com", - wantErr: false, - needsMoreData: false, - }, - { - name: "QUIC Chromebook Handshake[5]; packet 1", - hexData: "c200000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac824704489c14d28b48268df096f2db0c040c0ffdfb1817c4e1dd32005e87a74104515069ccd1b12f6fa3f657891bca8f0d27f4bb42b23c8310897ac09241f511888ac2e431e3ba8d9114b3f8bb1470324c8a0815dd1589686d058d03fa2669ad7367dc8877abce4700ae999112eb7cba755b20b7db66918021a36a66c60ecaaeb3e0859e3f50d14c1f8c3f56966043f437bfc16098deeb02e2236cc42d2f2802f4082bec30afc347a75f78c5e7337c52502e8e8d945bb4d792ef69419e57ee4e8f3465de4d0a49955787dff0756f1bcd98268919c112ae5e87ec333d590988e111724627143bf5a0aa69c77ee322b1240a65718e0033c4b70c6a5dc2d3248aef71598d73effa0eb397b79279ce846d9c2556fca28595eb3b196f40f052f0127711abbbc334571b8de67f7d39bd9437f9196bfe0650c4972462473776eccae42418b16edd5f45414e1a5e13300ae6705a49ef68e1748e876ce04fe611c973f94fcbe18b0c1a0ae506ce6eac268021e9744899c33ffe44dabdf1ea5413c20ab1013326255ae6df2d6861a7e914001e231ce4065e0d89fb439afb928d660cea26f99eafe65c9f89c5639f3ba8ffdcdf7a777d7859ca948f123a5c4f1491e22803754373ab0f1b605ac09ca6018c6acca7f52aad786ce96d9ae28d09a77dd88d77f23e2e33475973a23dea914fb049662800cfbed416b61835bd3bc22dace817b3b271c816ccf9603de2209067def5b8747112c49659cf6c5281402bed38a4176c0331e0841592c503d342188ac97266866a102864f76b0ff2adf781e0af84ae725fb6db418545dbb70adde5d4ea6d87f8c9b64b579d4f969830056dc0007cdad648035ccdf2c24264905422dfc1a85ac7f519f475351e046a27268312d4ab6a66ea55be01226fd5dfce06f804cafe85ad997946db3d029ef28ebdd36103c8fc05b522c1d4debe034523595cef576c771f0848a221d76c63dd044ea61a3adcbe5d4f0ebcd0bb631f4db1d88a609a1c2a6cc3de29ccd8a40e8a770d806abdae300cc178a32b4ba979652d0b8849dff252571f0b09935744c5b33628a190ae2eb43543ed8dee8539fd464abfcd3cf826aa18c4e84cd11975c5e3bfd0827f7cd211a2084c8626ed4e32bb9877f4801dabe695132b835e335563cb2f4c3e9377cac57f7766a10620f1c57c9f485d66918613daf8b257035ec91481d0076899c45abdbec38b63745428dee481c1cba81b0ab6e7efd6b0c431e018b6503cb13d4df18dbbff195bad1063d59ca5066e0733d3f499109111d22304066e7656e755518ceb6d862095ae54e532fad82f6c21c0c4d776dfecc516b00fa59e117e79481d3fd386c9b0c4d6fa4ec295a5b5b67207c3db20a206e0e31dad2c8dafb38e35dde331730bea33af32893653763e82565689009265db76b3b142d60f302a9545d82900aa6a017643c5c60583aefae43066ea3908e299612b078d06f102280dce4429461a42d80acdb6279c7aa190f9a8f61b485ffa430cbef199e1732da3d9b96cfac7ecc3b4959b86260eba763abb74c249180f67997967e716c95788bb6c04f182f40b795a929370c7fc72f1785f94cd36e3ddaddc15c7adb226cc131abc1863352caceb541d3924094db9c8a1c21c21640", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[5]; packet 1-2", - hexData: "c200000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac824704489c14d28b48268df096f2db0c040c0ffdfb1817c4e1dd32005e87a74104515069ccd1b12f6fa3f657891bca8f0d27f4bb42b23c8310897ac09241f511888ac2e431e3ba8d9114b3f8bb1470324c8a0815dd1589686d058d03fa2669ad7367dc8877abce4700ae999112eb7cba755b20b7db66918021a36a66c60ecaaeb3e0859e3f50d14c1f8c3f56966043f437bfc16098deeb02e2236cc42d2f2802f4082bec30afc347a75f78c5e7337c52502e8e8d945bb4d792ef69419e57ee4e8f3465de4d0a49955787dff0756f1bcd98268919c112ae5e87ec333d590988e111724627143bf5a0aa69c77ee322b1240a65718e0033c4b70c6a5dc2d3248aef71598d73effa0eb397b79279ce846d9c2556fca28595eb3b196f40f052f0127711abbbc334571b8de67f7d39bd9437f9196bfe0650c4972462473776eccae42418b16edd5f45414e1a5e13300ae6705a49ef68e1748e876ce04fe611c973f94fcbe18b0c1a0ae506ce6eac268021e9744899c33ffe44dabdf1ea5413c20ab1013326255ae6df2d6861a7e914001e231ce4065e0d89fb439afb928d660cea26f99eafe65c9f89c5639f3ba8ffdcdf7a777d7859ca948f123a5c4f1491e22803754373ab0f1b605ac09ca6018c6acca7f52aad786ce96d9ae28d09a77dd88d77f23e2e33475973a23dea914fb049662800cfbed416b61835bd3bc22dace817b3b271c816ccf9603de2209067def5b8747112c49659cf6c5281402bed38a4176c0331e0841592c503d342188ac97266866a102864f76b0ff2adf781e0af84ae725fb6db418545dbb70adde5d4ea6d87f8c9b64b579d4f969830056dc0007cdad648035ccdf2c24264905422dfc1a85ac7f519f475351e046a27268312d4ab6a66ea55be01226fd5dfce06f804cafe85ad997946db3d029ef28ebdd36103c8fc05b522c1d4debe034523595cef576c771f0848a221d76c63dd044ea61a3adcbe5d4f0ebcd0bb631f4db1d88a609a1c2a6cc3de29ccd8a40e8a770d806abdae300cc178a32b4ba979652d0b8849dff252571f0b09935744c5b33628a190ae2eb43543ed8dee8539fd464abfcd3cf826aa18c4e84cd11975c5e3bfd0827f7cd211a2084c8626ed4e32bb9877f4801dabe695132b835e335563cb2f4c3e9377cac57f7766a10620f1c57c9f485d66918613daf8b257035ec91481d0076899c45abdbec38b63745428dee481c1cba81b0ab6e7efd6b0c431e018b6503cb13d4df18dbbff195bad1063d59ca5066e0733d3f499109111d22304066e7656e755518ceb6d862095ae54e532fad82f6c21c0c4d776dfecc516b00fa59e117e79481d3fd386c9b0c4d6fa4ec295a5b5b67207c3db20a206e0e31dad2c8dafb38e35dde331730bea33af32893653763e82565689009265db76b3b142d60f302a9545d82900aa6a017643c5c60583aefae43066ea3908e299612b078d06f102280dce4429461a42d80acdb6279c7aa190f9a8f61b485ffa430cbef199e1732da3d9b96cfac7ecc3b4959b86260eba763abb74c249180f67997967e716c95788bb6c04f182f40b795a929370c7fc72f1785f94cd36e3ddaddc15c7adb226cc131abc1863352caceb541d3924094db9c8a1c21c21640c900000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac8247044898c283466f3b200164ad9b30e17b425e07f6722df94b9a77dd555fbf25e5b0bae4fef254daf03f156b78afd967614c78208deaadef3552040c055487804c047604c5d6846e67d33e5fb5f743a81f688220d6f4c87091a860885af95d2db27e9c5dd2361f8196f5c1ade8fb37e159980547c38c6a6ddd0e055fbbd52bd7615615ffca15a0c144899d25f21156c53cb3922d2ccb83073e26074025a3c39f64a67dc02044ce0d630d9120041b2233bd26282bd2d7d1a81d486b64cab6dba7fb4375e200f53523f714a066b96769f9b1dc7c14353fc1faa51c0aeb99507ff3ae90ad6f4bfbc9b9ea00a87f8bf8e213a84a9efe2ce624e629261d93c83642df97fe146bdef478cc92bba387c9b524ac83cde55ad8f4a4d8fb3c09c8245a50ac16ebb67d651110a10ad1f7dc74ed32b9d644bc4b229c56942072aae5c311059165ef6839e7cfa0717c7032b667b8618527722fdc10a4c0ea900e9b414521b89ca83f253ea414a410a8b0b13da25c4b618f2ccd79e5d2a7579b4c431107d56ab8df16125bc25673181a6bd5abb09941515806fda32659e5281457d8c093314da0087169781b306f348d3994d84bdef936bb11355c41ce02c359174dc2a366509c130c96ddf5f156e0eb7912ac56611cf4f59ff3a8785034e81738a53ebc7fb60aaed709d78980d39822aae7e9a9d985aee6c11252a5e984ff1d167b9d4dc5d02ca8ba1167422dfc0f68b3b5902f3fd032204a5020b14a288ecd845816575fca7ef13c85765385e9964a9f0f6e2e5c4e600b190b275cd91ee8a38ecf73d35bad7371c15fdb73059afb3178786e76750845232ef787767c6985ea9b9ed9ff73430b7afb8e0b66aa210ab1943b606021bb8f50c55534e1014003d947533c1c0bd0c16dfaabf4914497583b818e643a2d8bc32d33a07133b4eecf8bcbd802dfaae894ebd473338e074af1b3672c6f03c55e6c3885848a4379cdb5873d9ff3015f9ddf8d954b3cb4eb9f56f2e8ccb519dcba72c5832146011068d3074520370fc8008b62f3fdd65978865df1a85e58fb62315dc617a3367e6c9564fbf74f5d5015932d435f3aaaaed37bc7d14d974c0e15d899c684f17db3b4790bdeba076624f4f45a0cc1c6c078a527695110842e837ded310f8c6ddf69a18e455f09130d4b4136ca6d02eee0f103b9923adefe8cb36b8fd554a27fa7300c4a6173835ca2c4d38cc11e3849ebbf73c475b7d0713caed6e70f55b34ccffe03978f231d5f92bc1e2da7ce589e2cc2cd76183394469fa537cb927db14a383dc589cf10819eb90dbdb7981cd1eeb3a284c9ce55ed859fbfbe73ca22328073b534bc81d319b5fdb97e66d532b5ffc19516b4c70ecb68f963b053a82915f3f74c8809f082bbc89d97dff9e8c82a65ed4be8e2975b82f0b7c73f34797c61db4ea8ee273116564288563a71747cf31568317363dffdff1d9e159b2f9bcd3fe5be96a9c50ac885493cc16234c58cdf471381664c1e3b54d147ea5c779c9c9b06d5c493231d9c03b50c6cba315a5ee766a6e8578105306e0911270d347e8ade2b354037f507e4993708191d32b51a79f023f4fccabb7d2ac262004745cc528b8a6dc1d8849e9d4a0152ab326b1eab8a505eef65076aca156f7b9", - domain: "optimizationguide-pa.googleapis.com", - wantErr: false, - needsMoreData: false, - }, - { - name: "QUIC Chromebook Handshake[6]; packet 1", - hexData: "c500000001087e13dee82c592724000044d0a17c2a188dadf8c82a018cabf25d25211d9987189b2900ad36a8fa366990353230bc8d41ec5543e91e119a0fd35f72d320d670074d02f412a5418c9a3e25489db73b1f5e5ddb8fc8d6c51fd6a12dcade71e1fc8e89d2663e206185247847d218d695793a8a1054afd18d42a4feaa9bb5fae7651eab0bc5e9aa91539c311cbfd17f46d8894ac45eade657bd456ee87b4475f5e84a2c07571b082ecfdafd1a660075692ae22048d163067a60e06cf879eab20a464c6e44b242da94cc6b92e064ee6a52cb34c115056925d4615e51157da4e88f019ae144040827cf2cd2806b3f4018cde5f42e041ef55eab35fe65ff79dba1ddad5aba6221e93d4555510176463888666b1d1f53a2f3c71e8872b05717002717edd3a9c5c651be47a663ee877962623ee92310b2b1dbcd75ec2f31d9e2f0c87affe3ee083aaa0868c03c9b4e78660d8afa4e936f81dfcb2fc33dbc1ed3828195c8de3130a2757641652de23d270c8a25976546c46460d9635f499a650475a4288d1c62241f65ddd346e2bde3f4fb5fec76646f51d829b55e16a0975494c11101abb504f5398a761d59355a4843e14bfeced024239ca06717d27e932df5e050d7c54c9b3cc937f6b0731f0b1dc312b14cf4388bbc0c601abf8ca16ed7d9b597dd414156d75b486ac256f8989a072fe680961ac7446201aca359337ef0e58ffa2aa12bb3c9bd1a9f6237b5c37880cd450d8a95db7c1463c53ac4bca174f278be1a99fb75ded9fe282e0e44a03b51ad943cf8970e4ca7f8567d9a3883d07bc0ff6c11639ebc7614fa88496488cb2a49a158d0f008a6a03caf97d77c3d03a98c5a611b04f865d134e258f4094bd220e00bde789c3b3158e622d509073179c0725c7a14dde1cd76b4c6b61fc7ba87f05742f081a5c42dbe83445132b8ad1539ee36e221a9dc700ccab55e34688b082a1a8edc48335760f3d5fb7e92b548e7a09d11f15b97bda08ebb5ea355e274587720d7c1189e9ffbb00dba3c13da94d646e7742e3641d59aee50a5bd400cd992c5614325d8f1ff3529637c3f60adc1682fa6b5e26f3d52de73236680abde87ead74368e05c600a2fd291a592be00fba64eea78c9e1f5e55e61f691b3eb0ef17b7bd11f06427c89c4c930d18c0b28221a6c918322cae56397c7e2978bf253e7f2d987f4cbe66c44a96b45cddb9d5f6dae47bbc9e1f9dcbe6280e50c5dfc91efb469f408b28fed49c583800009dc8bfb4bc42174175df987a3be833582abd9aa09ba0425973de2ea9a4149a81ae1863e0c9f1b1075c26bf965dcbec2bea47ff6042495ed715b65fdd3266800994463c95960dfb6ceadfa07d58910d329fa7ef7a8f14da4a6d3b09faa5b17cbac8481ea46cbfcfb54f660929e268bcf2f86cd88a1b065dcc27f18110db9efb6fcf1eec62874ed3657b1d43419f39e785c510d239c021b7e97d258d789c90d39b434f1667495bd4f5dc5e0eb97df376d801cced0da3a85aab6ca12893d8622314b5d530f28ead33075891d0ee553d5bedcffb20fce9933ab5e117df816c96398f6a60c9e6f5b9182fd7d58869de01635b2c178ae7738beb81e318934ecd752393129fda6833718d6984d8e1a8d7bf52e9d93ad0902c0fbe3e66ae8305e43363d8996626646a684bebfb1809ac9823be750e84308ef573243b884d09ef294094ef256cfc13cb0fedb1e095ff71687c09a767bff308e562e1a6ce9964014a7afc8db2481d8d07486", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[6]; packet 1-2", - hexData: "c500000001087e13dee82c592724000044d0a17c2a188dadf8c82a018cabf25d25211d9987189b2900ad36a8fa366990353230bc8d41ec5543e91e119a0fd35f72d320d670074d02f412a5418c9a3e25489db73b1f5e5ddb8fc8d6c51fd6a12dcade71e1fc8e89d2663e206185247847d218d695793a8a1054afd18d42a4feaa9bb5fae7651eab0bc5e9aa91539c311cbfd17f46d8894ac45eade657bd456ee87b4475f5e84a2c07571b082ecfdafd1a660075692ae22048d163067a60e06cf879eab20a464c6e44b242da94cc6b92e064ee6a52cb34c115056925d4615e51157da4e88f019ae144040827cf2cd2806b3f4018cde5f42e041ef55eab35fe65ff79dba1ddad5aba6221e93d4555510176463888666b1d1f53a2f3c71e8872b05717002717edd3a9c5c651be47a663ee877962623ee92310b2b1dbcd75ec2f31d9e2f0c87affe3ee083aaa0868c03c9b4e78660d8afa4e936f81dfcb2fc33dbc1ed3828195c8de3130a2757641652de23d270c8a25976546c46460d9635f499a650475a4288d1c62241f65ddd346e2bde3f4fb5fec76646f51d829b55e16a0975494c11101abb504f5398a761d59355a4843e14bfeced024239ca06717d27e932df5e050d7c54c9b3cc937f6b0731f0b1dc312b14cf4388bbc0c601abf8ca16ed7d9b597dd414156d75b486ac256f8989a072fe680961ac7446201aca359337ef0e58ffa2aa12bb3c9bd1a9f6237b5c37880cd450d8a95db7c1463c53ac4bca174f278be1a99fb75ded9fe282e0e44a03b51ad943cf8970e4ca7f8567d9a3883d07bc0ff6c11639ebc7614fa88496488cb2a49a158d0f008a6a03caf97d77c3d03a98c5a611b04f865d134e258f4094bd220e00bde789c3b3158e622d509073179c0725c7a14dde1cd76b4c6b61fc7ba87f05742f081a5c42dbe83445132b8ad1539ee36e221a9dc700ccab55e34688b082a1a8edc48335760f3d5fb7e92b548e7a09d11f15b97bda08ebb5ea355e274587720d7c1189e9ffbb00dba3c13da94d646e7742e3641d59aee50a5bd400cd992c5614325d8f1ff3529637c3f60adc1682fa6b5e26f3d52de73236680abde87ead74368e05c600a2fd291a592be00fba64eea78c9e1f5e55e61f691b3eb0ef17b7bd11f06427c89c4c930d18c0b28221a6c918322cae56397c7e2978bf253e7f2d987f4cbe66c44a96b45cddb9d5f6dae47bbc9e1f9dcbe6280e50c5dfc91efb469f408b28fed49c583800009dc8bfb4bc42174175df987a3be833582abd9aa09ba0425973de2ea9a4149a81ae1863e0c9f1b1075c26bf965dcbec2bea47ff6042495ed715b65fdd3266800994463c95960dfb6ceadfa07d58910d329fa7ef7a8f14da4a6d3b09faa5b17cbac8481ea46cbfcfb54f660929e268bcf2f86cd88a1b065dcc27f18110db9efb6fcf1eec62874ed3657b1d43419f39e785c510d239c021b7e97d258d789c90d39b434f1667495bd4f5dc5e0eb97df376d801cced0da3a85aab6ca12893d8622314b5d530f28ead33075891d0ee553d5bedcffb20fce9933ab5e117df816c96398f6a60c9e6f5b9182fd7d58869de01635b2c178ae7738beb81e318934ecd752393129fda6833718d6984d8e1a8d7bf52e9d93ad0902c0fbe3e66ae8305e43363d8996626646a684bebfb1809ac9823be750e84308ef573243b884d09ef294094ef256cfc13cb0fedb1e095ff71687c09a767bff308e562e1a6ce9964014a7afc8db2481d8d07486cd00000001087e13dee82c592724000044d0b2c403d66eaa310a954540668e9edc4b17a321446ac931672ca3d9097b2854efdfa7a8f610be76592b56ef9154b9ab42f4dafc575a9b9572433fa2bba180194a32a72a92a42560ae840508317cf34015b1d7d88a633588ce4333cd12bec1f9d53fe905130eb136852d4f405ef66254a0807640fe415e6d667b9f5e2148826d5a6c36b3c44b822cfb7a1e76954610a522e1bc9703e155f8f1e0c705e411d09c5dec1183c61608174767408558da03290f5536682373a09c762deb829dfe3ee061ac9f509a2b3d20dbb77f262e9ba8a5ff50f895165742c90c4ff48eb0438d5ff8491700f97fbfced20e8e95ab9b67078ece6664527eb8a4e78944c07c6bc5c48776e141418c3b5a0e58d24114b7d65a83619525f5a10be53a55f6e3e65080cf42109aa2ec166fbb9079444ea6c809b5062227fed7cc81609a6d7ea471bc82cd0759a354aa896c7a8242c3d8c845aa52225bbaf7546a0de6510189cd2852f7b70a29687eee6a3a99a3a288f3c3672a72dba2843fcee320e18ea906adf1629cc7f8220b6be890a8d37f4093022717d8c6d76080d07798ba74e9cda1a4c26189c367a6d12da14621b93fbfd2d2365465350b5864e4981955750c6d4038b74644827cd60c8cad0d6a18869f99d61b214becedd8f71a476c2a1a0ea8fade7b15de6ee522b6b90d0cc4e6d40eb85d9b5097e2969e85eae6710285016ae47fbc858d63e13d882721aaef3cbe0a6e4c4910e8b3e465fa66a738d8979a444b4a4804baf5b4f9a5fc8438f4991c32d280c5d273d0a1e0d9eea30d003162cd313780a5b000f70fb7797e6111b354c3deac241cb816328b77d71584852966392cab9b1a3d64ead878a2cfac085452397ba17b5cddf96415c31846a80fa9a5a21fd5f9963324ad75505d4b5c70903ec3b5f91c3460d88a7d1c6b111a5206d11accac8a2115e1ce8e834b01f48e041dcd71585c6fcdd3a2e83ec2ff1a2b85e75134ec966afec023d375a2a8bcd54ef0c42d50e20c6cd9b9fdab63785e62c5a4ceb8451c560b647a6cd87698e56e0be4e2990337f45f1cb4c73f0dbf98da1158d75df6eb60ab7a61d836c7b7f3e5c809b850a5cc5369ddd4e455da5e88179e932311873d177febf6226a378c72324ea710e10ef74f6462e7dba25b7df336cb2947df749ad0b8455f3c5c9ba5c0c8eb1e665fe50451c928ce87cc81a0ae2102e7a4fc297392818db885943112fae3c4547ea48c89ef9cd44c2edba4856dbc72b956e4fc6a875bdac57b1cd2378ddb9322e5a1d1844977ff2a6e94d8a00f4ef0a7ef7949f0840c6cc781252830c70d37df9846a69eb95bc733ec420b7b3724572521496da27bba1fcb73c28eed1eefb094283ab01284ebd82004e0be9977f23fbf135b8162ac41a2bf9e1b941b05cb52c80279775070cf2f851bb0168235c97de47880d2d51f1d5ba8327af33f16f49d44326b0d08d20ca7880ff88a14d3201452d7fd452b24fc3b69b89c5149795bc4d0bf51b6ace2c9146048e5ca0259b4955a069a16d5a73e72248cec44b2ca542dcd326aca3ba53bd48368719f44d53eddbace11ff28c11c7fd90c38966752532d3f81325d1682839b2da3d69acc85275c71ef3b10c4d983cfb0cb464f554d9360b0d6ec8fd25b85824350866207eef9e2c66e43f5d08e86aa6186b36d9ece72f56e06ac8e51a1b53fab4cc496e69307e9810a9b5cf960f306ec54131dd8e917a5861ba92f6885e41993", - domain: "lh3.google.com", - wantErr: false, - needsMoreData: false, - }, - { - name: "QUIC Chromebook Handshake[7]; packet 1", - hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8b", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[7]; packet 1-2", - hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8bc4000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544898bcc839c7364f9518287bbf01c06e9fd67f74096cab78bc885ad264ca9151a48420ea11d5b9e3bc899cc0d663509c926bee5ef2a389a9945f5919677472b3245a2a68ab91654ba5f75026ae738a9e10b6b7439503961bedbfa2494099a9bba3b9595f70cacd8950abd5d3b8a85ac61e6482847e0ac71cbd63df462f4978afc7084ec5c9e65f3b57ad22f802d699c94f260212a61db6e6958ed60356bf991d3af9582cd1724a90f08e869ebd7f29fc3fb9e1198819a967096de1bdeea692fade6229b41c5efe6cdae313014db328e99bae1f323584a308f575b923a11185634b24c8cc1d608303c8ca5c1ae3d54b8b7ecb689d5e0e22323c5e47d8f9093080bcf1553f7fa238e156658ef50281f633f184e47992645c82ab9985b9b13f97f0bbd9db94285c131fff5870ea38691ade2e017ee6417a118a9b31f5b85ba2c76163f8668d90d448976d9d37238605d36b0b7e7abfb0e9c2866bb11b128bb70f003aeb77f439c6148d28719a4f336f4d622140faff82595233cff94906695e402b9115ba7b99f7d832f55d8de481af808ed9d48a493c3301d2b633cdcdc7a64acefaa9408b99a52768edeb9824cdb49257c6d3ea3f23763f70b74fc46252a97f7b3e173a93e81cca50fb12e8b0ea1083f9262e1607156296b5ff85e1531335441c90b442aa5dc616ed7b520bc040502280a0bdb011d6e9dac154587f033b3e9434cef567b724bf5203c315ef5e283028ec72ff33736499b8d326cd643e3a37d53e70b9a2384ac6037f488075eef8b2e507774d3d450ea4b1b65c1587eb2a60c6aad0ae20745a2c50f8acd8bc1e25740b4e7ab4b03a59731ccc487831b46ca0211264f4fd4a498e145aca0d7339663d29a7f32a14fc2b62a32f6354abf2d7de5ed9d7e0a1d6dbbfe14af25ca8c927dc8eeb2618f308eac81405552f1bafcaaede51f560fe5eb6aa78b8650ae97040b46469503c85994ebcf7ffa0222905657111325f861d380935c2a5b9ad26094e85b92327f79f66b4a2f8cd674344f931f0e056ba58a084a1026bc422b80ec11b17de1ca965e8ac9ede1464588303986ba8fabb395b55de5580131ae3bf61822e4e2817dacf765e034542e435c4e9ebc58f21cfa7901dfbcc2b2be98cc55fa6d9e0030e17a32abf84d551a74f7391dd204847c25ee6382f6374b8ecc5b29b598b9ba562e0a6f25ebb932570ae8ab7ef7d06e444fdf79845e88b3132ffb6e1f330e3424272da082486aba357bd254ef0738bb7f5c9db73d2a9ba0c9afeb34e09ff0e20bd44ba1a46ad3081db2f750d00b647756dec1bb41032e1aaf56f58d7046102aef6ae40517f9cbc148692401ad06ea4ff6ed3e5c8a0cff8f9466a3530a99cfb9b5a857a967675df0cfbe3b798fdc2d929fa4c86e3b3e3c45b75fa5db6c15953f25bcd025d7efc3172b2206a200128f6d8caca97154b2608511b35b0cba9a550d8f791552faed64036cfb8498dc60492207ab81c3f3edeafd9d3acf5bca2f2735117a273c70345d3b7e289b9948edda3d5ebb8adde7de2451fe2d942d92ab383dc9a9adb6fb9e8cbc0b73d852f176e0265a6107e6a59746e2f2e7203fd445e5fe278d02cd5dac1b7988e205c37bc5f35dc27572743810453b9b27e55c", - domain: "", - wantErr: true, - needsMoreData: true, - }, - { - name: "QUIC Chromebook Handshake[7]; packet 1-3", - hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8bc4000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544898bcc839c7364f9518287bbf01c06e9fd67f74096cab78bc885ad264ca9151a48420ea11d5b9e3bc899cc0d663509c926bee5ef2a389a9945f5919677472b3245a2a68ab91654ba5f75026ae738a9e10b6b7439503961bedbfa2494099a9bba3b9595f70cacd8950abd5d3b8a85ac61e6482847e0ac71cbd63df462f4978afc7084ec5c9e65f3b57ad22f802d699c94f260212a61db6e6958ed60356bf991d3af9582cd1724a90f08e869ebd7f29fc3fb9e1198819a967096de1bdeea692fade6229b41c5efe6cdae313014db328e99bae1f323584a308f575b923a11185634b24c8cc1d608303c8ca5c1ae3d54b8b7ecb689d5e0e22323c5e47d8f9093080bcf1553f7fa238e156658ef50281f633f184e47992645c82ab9985b9b13f97f0bbd9db94285c131fff5870ea38691ade2e017ee6417a118a9b31f5b85ba2c76163f8668d90d448976d9d37238605d36b0b7e7abfb0e9c2866bb11b128bb70f003aeb77f439c6148d28719a4f336f4d622140faff82595233cff94906695e402b9115ba7b99f7d832f55d8de481af808ed9d48a493c3301d2b633cdcdc7a64acefaa9408b99a52768edeb9824cdb49257c6d3ea3f23763f70b74fc46252a97f7b3e173a93e81cca50fb12e8b0ea1083f9262e1607156296b5ff85e1531335441c90b442aa5dc616ed7b520bc040502280a0bdb011d6e9dac154587f033b3e9434cef567b724bf5203c315ef5e283028ec72ff33736499b8d326cd643e3a37d53e70b9a2384ac6037f488075eef8b2e507774d3d450ea4b1b65c1587eb2a60c6aad0ae20745a2c50f8acd8bc1e25740b4e7ab4b03a59731ccc487831b46ca0211264f4fd4a498e145aca0d7339663d29a7f32a14fc2b62a32f6354abf2d7de5ed9d7e0a1d6dbbfe14af25ca8c927dc8eeb2618f308eac81405552f1bafcaaede51f560fe5eb6aa78b8650ae97040b46469503c85994ebcf7ffa0222905657111325f861d380935c2a5b9ad26094e85b92327f79f66b4a2f8cd674344f931f0e056ba58a084a1026bc422b80ec11b17de1ca965e8ac9ede1464588303986ba8fabb395b55de5580131ae3bf61822e4e2817dacf765e034542e435c4e9ebc58f21cfa7901dfbcc2b2be98cc55fa6d9e0030e17a32abf84d551a74f7391dd204847c25ee6382f6374b8ecc5b29b598b9ba562e0a6f25ebb932570ae8ab7ef7d06e444fdf79845e88b3132ffb6e1f330e3424272da082486aba357bd254ef0738bb7f5c9db73d2a9ba0c9afeb34e09ff0e20bd44ba1a46ad3081db2f750d00b647756dec1bb41032e1aaf56f58d7046102aef6ae40517f9cbc148692401ad06ea4ff6ed3e5c8a0cff8f9466a3530a99cfb9b5a857a967675df0cfbe3b798fdc2d929fa4c86e3b3e3c45b75fa5db6c15953f25bcd025d7efc3172b2206a200128f6d8caca97154b2608511b35b0cba9a550d8f791552faed64036cfb8498dc60492207ab81c3f3edeafd9d3acf5bca2f2735117a273c70345d3b7e289b9948edda3d5ebb8adde7de2451fe2d942d92ab383dc9a9adb6fb9e8cbc0b73d852f176e0265a6107e6a59746e2f2e7203fd445e5fe278d02cd5dac1b7988e205c37bc5f35dc27572743810453b9b27e55cc5000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544894fb2a6f735bea394066ea4ab5d3ef6f419c4bb099bcea8fd7e36d8ec14fae027d0c84ba8c38a5ea1e2e9ff24ec334dee8085d1fa9d88df2c5ce3dddd981f7b2a68011f2d4034f8e4c6f43e645fc71e19927aec56045f0ef72d9da1942dbe3e42248c2f695525c8d8fcf6dbc970c2eb5d607e07dce5c8c66d4de07de6b2c35bd3bfa12cd4e4fcd4cdda3e0b2ff7575d406db96502f1ec4d9b5748215fe2a4018c7dbdb5ead1ddfc06da5233ce359e4f3924f2af1b80c7af9d13437e0107f7e240e485458a3a656e31a543bc5a80f6a598dcbbd87ff9cb4ce6842abadb72d62ae03e6d12b7f43ac1805e408d738148fa3a5c34deae7378d7d7309a7e09f5bac848f4c031693fbe3382a2de66a9d007420512e24b8a78a9489b30794c7cc51dd751c1cdeb2870b7c4a9b9606547f843c1a16d9be986b2f3e3d2f73f89c8e15da9de3dfdf7a667ced977332c5c62fd83d3c9a5e9718643e716db8f1b6fb58904ca17c24c9a4a191ec42be4c405fb00e443429582ff712dfdbadf1bb7e2d2abb0cc14f39c13a3b7013c62fd99488f043a6aec3ed7998b943ee24905fc915e3144c54393fa67ff34fdce2e48bba044f13ee1cd9c4f59a1a41f7fb7bdd8daee73461234f7cce5d54b078c3ad2b0aa37850ed4bb24f4d310d4ce75ede546ed6e73c0ee495ff8ce4b7256e8f43949539bf9df6e8d0fbf066fb506020c5a72e5e8b686641b78b365474c65fe9d8dd41133e27326fe82744b45b6ad1170433f3746647a68b824e94a213cda4c02f78465acbccdccb5bad1c70806d5a5c98c94338f88bf980b05b72cb82a4fd5b2a8586a5e5c5f2760115df595091809cfd12829f09622b53ca1d3809060aa7ab5d1f3640b3c2792a55c58fc3e80ac5d7f39ab5774a80fa1fc70461d396fa70dad1f90598244ce4197cb3ea42dce4afd61ca8ef93c8bc254d347872db21edcc3857bcb8dbe627508aa4856bd7d46e512db071905b3db100f425ba9f7181f0cce005cee2a95ffc190ddc1939e7049e58791b0e186433b0409f5a49e4e3262690a5160f8267c9099afdc58182236833fe7f825dfca34b08801345c1592bbab4964b34d7efa6c9d92e0106ede9a10fbf2a1be32f61f914211c3caa8b4c14edec5f9c139ee14789fe7d6634ede9bf9789caa60f5bf30b092e65ff95d3b32cbdc3e5842e3b16b935d31a3a0963bb0fe60f41efb6590f24eaf5e84006b28b3c755203113237e43fa70a37a009f71da49ea3f8097914d6128ee2b18adac49b5111fd3d18db9fd61ef8a2202fac5cfce646ccbea7eaaa81df0f1b7243465de15a3900143f479852f0e40bfad434b96eea3941f527b0d31c3d8f43188b911140766b5d7146feb93bf4da1ec47023dcd8f89863e487ba25c3105a4e43c4ea90f479eb0f774f3aa044b817f7e69b5dd1b3954e7dcdb2a6c4d191d5b9178262449413310f3876dd93145716781cb077025deb37d23c35e6fdf867d5353d10303b9e60efa50e9ecd013cd3f5270fc0e117a19ffb63038c594190018bd9c1c18a799f548c08a3e6f768de0de344cff160689fed73aa7fc4edc26f77413145775745c25fc2c9da5b62e24eab9b21895cfcda6e6457ae9fdfa6c54b49b0d160dad0aa7f8cbd3820a3098", - domain: "ogads-pa.clients6.google.com", - wantErr: false, - needsMoreData: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - pkt, err := hex.DecodeString(tt.hexData) - if err != nil { - t.Fatalf("failed to decode hex string: %v", err) - } - quicHdr, err := quic.SniffQUIC(pkt) - if (err != nil) != tt.wantErr { - t.Errorf("SniffQUIC() error = %v, wantErr %v", err, tt.wantErr) - return - } - if (errors.Is(err, protocol.ErrProtoNeedMoreData)) != tt.needsMoreData { - t.Errorf("SniffQUIC() error = %v, expectsNoClue %v", err, tt.needsMoreData) - return - } - if err == nil && quicHdr.Domain() != tt.domain { - t.Errorf("SniffQUIC() domain = %v, want %v", quicHdr.Domain(), tt.domain) - } - }) - } -} - -func TestSniffQUICIncompleteServerName(t *testing.T) { - // 2 packets - pkts, err := hex.DecodeString("ca0000000108d799f48baf472fc3004047005411dcdaa24bd4c3cdbef653940a1ea0a671bb8c23222a8502bfcbd0a335af016df330b1190e4b1efd420aa82c4414a7dfd7b1aa63aa7bd0269ec8ed0d24895a467b737fcc514488a8142241b81c4952fb32e9f123f3f8d1be9eecc1340d7fa747c6f4838810209d51ad453fcec084ffaa382f9fed010a0585f8ce73c36dc6c628df940f72fb5b8e3e06f4612128d3b8f87725770d81305489a3a23cd443fcc668fece0ec9a909c82cfdb66819ce031422c3edd49ea72b6216efb940b8c186e2510b0f93d4cf3b6434ed9f5817c5a5a19fa861d24883bd48ba296df81e887fcb9ef18013ab1070df268049f3d35e8ddfffd2b100e4eb687176a02e7975085caa138e9df7cb6f9862356faafba3300f586df09af3b4e3eec15b6d1f14b34fe4eeb8e762ff482f3b7b0595c16d3eb20cf9c892b3f7c6449e51ffa33f3dc877d32e572e10ba4d5a320117110b150bec4a9434103de963aa2de1b22eaa992b59a8583c7a2d4e5743ef98a1b1f5f4aaeb2b49dbe441c331e7cb028e70432ce0890f78d40671f13d5feccc4ab264ae903affaa951897128d598ed8e8542294aa03abb0a2a7ec970ab874387b8777827c4847bb29ff93d0427c1fcc581407465611e4400bfeab39de4c4f65572365156421503706d66138317e514214db27303ad8b5b17eb443e959faad1ccb3d3def5f3843b241dd9a60378cd907a02cc5c6e8527715c7734079cf9143586ed471587690816133fa8f09b8421f2de6ff1f5cecc134ac404e53a367b57fd4f3c85fe7e121e7094fbf93c06d427cec30cd519ee43a6018a8bb8717c05d7660b379101a182bfd3018d485d5b46e81d39445ccd4d8baefdd7590e664f2289f11bbad3d35ea6d70e157aaa7f9e91a692794b85c5b86db33863151468d32792ecfdb2aec52784ded5aa431fa9a7337ddd9e95313a897fe5ca6ef3c7e0cf3d41f7f104506f8695f2ba307715022e5e98472d6ff24125004d14db2c7a4cc71ca39a9874bfb93b64dd54ef451097059bbdfa96aca6c6c01e1f000b8d35bdead1502874e5d80ac00a79593b6d7e2fbee406c2212b4abd7b7e9f0037518de232cd6559443f4e3f0f03c17cef616a74992f65754bb6699b08c0eb2ec59508086e496e070c6239a73f11a9d14a727b10188ca97ebf04851fa475ed1774836a94cfb6543c33888dfc250100e9992b0cca8bd27ae8552541f1b3d81546f15b740e4f07b41af864769fe17290eb7076c0c1f938ce56a45ead6deb6c89164f829543414a1b8cd2361c3d9b22cf85d4ad0d4221dcc87533506e32c1cfdc8240636b669f97f7c6e150ea3753fcb63a7b9eedb5615b257651e090b567a20fdc5c167ad58fa940985572db004db2b14709a6663c1fb79dc045e45cb7228f11eab4c60a48fffc702cd74dcf5aabded8355a5ae1a5e6325a3afff608e53af3aa944437139c438e6200c75bb27c8a8ce7ce9f9ec9723c4423e9e49c807b12484783ed79fc501b544f03e4b29bdf0c74eb055dbac9b78fe87a66efc819f9d2ea637dd478b920b7196232b970ed092050ba0621329aa2b3e26184176137a9c6283e4cc2c03c07e28d2da7b0485da51f131686f4dd6b23735c66316142f48781885c35c9940054a4b9187a20914bedd1c48e94af358994da5c6060dd224b0ba84ddd5e95f395fe941d464e2cccd857a180de756d32407540c3611b649c7a71cbf5a492bf5c8d4e112a743498ce83ea3e94bcaa6c00000000108d799f48baf472fc3004047005411dcdaa24bd4c3cdbef653940a1ea0a671bb8c23222a8502bfcbd0a335af016df330b1190e4b1efd420aa82c4414a7dfd7b1aa63aa7bd0269ec8ed0d24895a467b737fcc514488e7151df796764293639665547941eaaa7d83dfc9d42952583a821611921cba9e0276f9244f197e011b6c2d898255802c81c8792dd68d03cd8982ee6146c8734814e9640e0999312f97e898db60642ceca21700be9e698b5e577062148d1ac840e737f7bc4f18f9ffd13e656fca1517c96d0e0c607b67d7e75795f62131255b44c2178d896192348d5f053060fa21c855236401e853e435fbc71a428c8e126cd8393cb860193fd6304d9576e8a8ce7f6959739afa7d7ffaf86516afb061ade1eef603e8e5c66f002cacbef25546e84b59be707c547837f7b49471a59325ad58a4f189a525a3a354a4be5ffd3db34ce0585ec470f78f0ff6eb807989d5998e2fccc80bd651de654da3581b6c8a858b56c4df47c3041c50d74f0b3a3a5a325882cd356d0c46d6db74559cef934a1655c65a06daff218244f5f4e95b96794e45d371a03afee2c89768fe3d7bc134844f45440620a88d6ddb5d07b7f6c9b2adfbf5743c5c586520504febf14ee4b216f8186ee1bf9802ccebd4dac519bd337a554940a3873cd21ef32f3d4d4fdfd3eaabb172b5314490b3587a9ca2dc12637fa80dcd526859bb83d54f67dedeb173470e052beec9edba4b41444d31253accb3381237179a5381d437fc4d47c0a82f1c9da8c76ad3eee9417bf69acb8d530ac824e65d796b84bee40b14daa9e80c11e466f93233e1768ecc6c0237e71b901f1faa093bbd9e5324b2a160d02f09d4590392279a15c73edc4d1d7501ebad992fc8f1c9dce96c190879225dbbb09d20532f9dcbb845e3484c87cb8c3b06fe892b07bdc60926e3d4b5c9024acde5abed0a72686d546da535a2122cfd6d6488a491dc9612b90f3f6709835e83f59fd612c2259c3ac944fdae87ffd744176a92fbea1efd037565a39d5bc38991cfe6617135b43cd6c6484a3b228e10d3f8184227a7ee63121be20f099c4264461d52aecf4b8ee1a4d0ec41717f9a68e1394544cf3be0a3a171ece35c879b67afdaac49c12731e6dc5ccaa49fa72610901036a7f0752951077cd9f1d77eb6e4f12c16dab08e1a9655fda3ba0e80c34c767f40e61491f706ab9d1b50f48273ca4e37708d075909a3e2c4b173510114d8377b5300994f9a4f794db2781b98c2e9d0056fdbe87b249beb3e7301c86efe6b32242b4c7fabef280a1ca7bdd6abb38624cbebb01b6b75b07d0cd9090b2c296d0e330b9008788dd6f7e1af1dd63b02408f1a67b57be3316152270bf90c36e9f74322c9caba5adab3309e5bd7c44d3c2ac77329e167dafda7395e37313723fb550a6ab96729a9fa8f9e4a3e5957bffa3302617d48fd38196635309928c491ca974a112768de16043fc618001780b49a0bac2ea9fc2607ebd1f25b8ef65a6cab42d16c9184e23af496ac5cfab274d9557006d9c52bb56cd8c019ddf085f260a0582ef465c73f97fedc02271dda53eec20e6aa50503357ed1847f7d976899cb24e5dd42dd87ae48db3c9335b38957fc0f86a6ceee590b1cbee41fc6ace3189886bd86f9b082e66fb263f1a98dd8e564bff9ec412ae79e29fd519ac96e9c5b446b436b78a6ca555c496ca5c5e73c2c1c5837b13f7a953d57738440d4ed60efd4701bbbbcad79e1697b5724695f52dab8bce02e7f") - common.Must(err) - // The first packet ServerName is incomplete - pkt1 := make([]byte, 1250) - copy(pkt1, pkts) - quicHdr, err := quic.SniffQUIC(pkt1) - if !errors.Is(err, protocol.ErrProtoNeedMoreData) { - t.Error("failed") - } else { - quicHdr, err = quic.SniffQUIC(pkts) - } - if err != nil || quicHdr.Domain() != "play.google.com" { - t.Error("failed") - } -} - -func TestSniffQUICPacketNumberLength4(t *testing.T) { - // packetNumberLength = 4 - pkt, err := hex.DecodeString("c60000000108fa6cc47e912693590000449e5ea65fc129945b27f59f0b18f737a53e44f322681ecec36beeb943293db0ed751bd86413a840f9b9e9be718f3e2dba1100a8bab3a21e1541580ffd98e79aa89fa723e8d3a46f7876504c82b9ef3a081b0f8cb551370d9801ee86da77f09eec5aa19b7bf580a80ded3c9c83378285177115fd30b350c2a596ae265b3b538a81c183c0cfd13eabecfbbeb38416a5b19259731b838842c0eb33e646b9bb1f672043e90de33c3442151ee8db7d9cd66238f769f4486432ac28785a5083c616539f8320321060f64f9a0dc6af718754d645892397ff32956c4c1c97d0d9e44cdfa8d1a0ad90c3bbb7810b2196d638fd772a172a9510ea12ef12fe4050c5678851be26ec6ea6ab11824cc86ce071d110f72816166c01622c0207e9d97f8867ec7c63149e974c5a81db9cb5e0061cff2713538daa1c9ad1382ab7d883ecc85158dc76587793b258b4b0aded3f4c12b515a9183ee419b304cf748fc321f15b3f80cc53da1b889d1ac06b996d35e8d01306885851ad253083f37d0d588c9f619da25f6eb8360b846bc26913af616e2c3eabce9dbb61f7dc96b6dcb79e19905ac9ba8f3938d03f8a3647403dc919f37bb585a0d67b7ce955547d15c82eed6d94b04dfa009eaf8b30448e1450043c48845acb4fefcf29bdd55ec14e395d0e8cd8400ffdda5a58747c6e8a66d0dc5fb25f3615081b2b546e004079573e99290f5daf72705ca495707468dd26d94c7f04d7e6f89d8148ecbab67c8c0062984e0ba02539527370a2a157a58eab342ad671641812ed35b4a52ba07d244d9b5d64e29f012133d21fa4afa31645c21f6d836ad937bb75f7177768b5f94bb77e95aaf9a85f8fb7e599d482724f694cf5d7d3f61bfca892794bdcb3de7a5f321db8120560bc32b8839c0a5994917c151cc6bd4c1614c5f117e637c19dab7cff28c4848c3b328eb97e49cefded2d44a824f2705807770c2ef9dd07f0fe0198659ad062e1889e280e5f3d1c52a92ef27d4565ebae9b9a18a803b70f38e5db237ed99583d8952c79492e35e1f1c41664f1f45566126a7ec44a90004b015b893aa805fcd772737fb8dbbe7af56b9eac288ef6cab7cbb6f7a3c0b29a43bc84b6280def0f7d727b3238cba3eda2e2d110de87ad0f10e25a60783cdb0c05116df5359b19b40007812b898d03dc1d697690761856d785b83ac95778db69c3df7a8f0e092ee6ed2c9c189ebe40734b02cee2d02599e931d4cc560d38a7ec355b9f339b932613ee18f8162e8d3cb81301bfc6d726b7c26ec96d5edcaf0de171563482ed2f2de3001dbca2aee1029fdaece4340fd2d5ae8333819d5ece7c9d3f77f99a81fccd1fbcc3ea585c1538e0363141e0fd20338a193695377987afacd0baf1f0dce11b4bbf29965109bddb508e8a0974c08906d4ea340d51af376c3dda55bf97e3ba5d688533980e12704679bb18d0ef4ddd5b3af1b7676528c4bf4a84c84d40892715c0a8808ad51d6ebcc6469da708d6953e9ddcfbd19bae90e9b078f1b6641401b979304b0ef52b1441e1797ed366bb0519ca8bf9c6eba72518647d0a1400ca66a20fdd8e3ff06ee52c199bd8f941b1722a0bbf8c15447452788ba81a68431f735d99e8a80691ef64d1bf470350c8878aed3e2421223d0ba6a3d84928c8e6db0972263df9da49b8f5") - common.Must(err) - quicHdr, err := quic.SniffQUIC(pkt) - if err != nil || quicHdr.Domain() != "www.google.com" { - t.Error("failed") - } -} - -func TestSniffFakeQUICPacketWithInvalidPacketNumberLength(t *testing.T) { - pkt, err := hex.DecodeString("cb00000001081c8c6d5aeb53d54400000090709b8600000000000000000000000000000000") - common.Must(err) - _, err = quic.SniffQUIC(pkt) - if err == nil { - t.Error("failed") - } -} - -func TestSniffFakeQUICPacketWithTooShortData(t *testing.T) { - pkt, err := hex.DecodeString("cb00000001081c8c6d5aeb53d54400000090709b86") - common.Must(err) - _, err = quic.SniffQUIC(pkt) - if err == nil { - t.Error("failed") - } -} diff --git a/common/protocol/server_spec.pb.go b/common/protocol/server_spec.pb.go index f196930e..ea12a4bb 100644 --- a/common/protocol/server_spec.pb.go +++ b/common/protocol/server_spec.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/protocol/server_spec.proto package protocol @@ -33,9 +33,11 @@ type ServerEndpoint struct { func (x *ServerEndpoint) Reset() { *x = ServerEndpoint{} - mi := &file_common_protocol_server_spec_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_protocol_server_spec_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ServerEndpoint) String() string { @@ -46,7 +48,7 @@ func (*ServerEndpoint) ProtoMessage() {} func (x *ServerEndpoint) ProtoReflect() protoreflect.Message { mi := &file_common_protocol_server_spec_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -123,7 +125,7 @@ func file_common_protocol_server_spec_proto_rawDescGZIP() []byte { } var file_common_protocol_server_spec_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_common_protocol_server_spec_proto_goTypes = []any{ +var file_common_protocol_server_spec_proto_goTypes = []interface{}{ (*ServerEndpoint)(nil), // 0: xray.common.protocol.ServerEndpoint (*net.IPOrDomain)(nil), // 1: xray.common.net.IPOrDomain (*User)(nil), // 2: xray.common.protocol.User @@ -144,6 +146,20 @@ func file_common_protocol_server_spec_proto_init() { return } file_common_protocol_user_proto_init() + if !protoimpl.UnsafeEnabled { + file_common_protocol_server_spec_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerEndpoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/common/protocol/tls/cert/.gitignore b/common/protocol/tls/cert/.gitignore new file mode 100644 index 00000000..612424a3 --- /dev/null +++ b/common/protocol/tls/cert/.gitignore @@ -0,0 +1 @@ +*.pem \ No newline at end of file diff --git a/common/protocol/tls/cert/cert.go b/common/protocol/tls/cert/cert.go index 00498579..cd8e56f5 100644 --- a/common/protocol/tls/cert/cert.go +++ b/common/protocol/tls/cert/cert.go @@ -13,11 +13,12 @@ import ( "time" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" ) +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + type Certificate struct { - // certificate in ASN.1 DER format + // Cerificate in ASN.1 DER format Certificate []byte // Private key in ASN.1 DER format PrivateKey []byte @@ -26,11 +27,11 @@ type Certificate struct { func ParseCertificate(certPEM []byte, keyPEM []byte) (*Certificate, error) { certBlock, _ := pem.Decode(certPEM) if certBlock == nil { - return nil, errors.New("failed to decode certificate") + return nil, newError("failed to decode certificate") } keyBlock, _ := pem.Decode(keyPEM) if keyBlock == nil { - return nil, errors.New("failed to decode key") + return nil, newError("failed to decode key") } return &Certificate{ Certificate: certBlock.Bytes, @@ -115,7 +116,7 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) { // higher signing performance than RSA2048 selfKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - return nil, errors.New("failed to generate self private key").Base(err) + return nil, newError("failed to generate self private key").Base(err) } parentKey = selfKey if parent != nil { @@ -127,7 +128,7 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) { pKey, err = x509.ParsePKCS1PrivateKey(parent.PrivateKey) } if err != nil { - return nil, errors.New("failed to parse parent private key").Base(err) + return nil, newError("failed to parse parent private key").Base(err) } parentKey = pKey } @@ -135,7 +136,7 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { - return nil, errors.New("failed to generate serial number").Base(err) + return nil, newError("failed to generate serial number").Base(err) } template := &x509.Certificate{ @@ -147,34 +148,27 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) { BasicConstraintsValid: true, } - parentCert := template - if parent != nil { - pCert, err := x509.ParseCertificate(parent.Certificate) - if err != nil { - return nil, errors.New("failed to parse parent certificate").Base(err) - } - parentCert = pCert - } - - if parentCert.NotAfter.Before(template.NotAfter) { - template.NotAfter = parentCert.NotAfter - } - if parentCert.NotBefore.After(template.NotBefore) { - template.NotBefore = parentCert.NotBefore - } - for _, opt := range opts { opt(template) } + parentCert := template + if parent != nil { + pCert, err := x509.ParseCertificate(parent.Certificate) + if err != nil { + return nil, newError("failed to parse parent certificate").Base(err) + } + parentCert = pCert + } + derBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, publicKey(selfKey), parentKey) if err != nil { - return nil, errors.New("failed to create certificate").Base(err) + return nil, newError("failed to create certificate").Base(err) } privateKey, err := x509.MarshalPKCS8PrivateKey(selfKey) if err != nil { - return nil, errors.New("Unable to marshal private key").Base(err) + return nil, newError("Unable to marshal private key").Base(err) } return &Certificate{ diff --git a/common/protocol/tls/cert/cert_test.go b/common/protocol/tls/cert/cert_test.go index 4245f3d6..f4bfe090 100644 --- a/common/protocol/tls/cert/cert_test.go +++ b/common/protocol/tls/cert/cert_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/task" ) @@ -42,7 +41,7 @@ func generate(domainNames []string, isCA bool, jsonOutput bool, fileOutput strin cert, err := Generate(nil, opts...) if err != nil { - return errors.New("failed to generate TLS certificate").Base(err) + return newError("failed to generate TLS certificate").Base(err) } if jsonOutput { @@ -78,9 +77,9 @@ func printJSON(certificate *Certificate) { func printFile(certificate *Certificate, name string) error { certPEM, keyPEM := certificate.ToPEM() return task.Run(context.Background(), func() error { - return writeFile(certPEM, name+".crt") + return writeFile(certPEM, name+"_cert.pem") }, func() error { - return writeFile(keyPEM, name+".key") + return writeFile(keyPEM, name+"_key.pem") }) } diff --git a/common/protocol/tls/cert/errors.generated.go b/common/protocol/tls/cert/errors.generated.go new file mode 100644 index 00000000..6a4677f4 --- /dev/null +++ b/common/protocol/tls/cert/errors.generated.go @@ -0,0 +1,9 @@ +package cert + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/protocol/tls/sniff.go b/common/protocol/tls/sniff.go index 11660b92..e7661fa7 100644 --- a/common/protocol/tls/sniff.go +++ b/common/protocol/tls/sniff.go @@ -3,9 +3,9 @@ package tls import ( "encoding/binary" "errors" + "strings" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/protocol" ) type SniffHeader struct { @@ -59,6 +59,9 @@ func ReadClientHello(data []byte, h *SniffHeader) error { } data = data[1+compressionMethodsLen:] + if len(data) == 0 { + return errNotClientHello + } if len(data) < 2 { return errNotClientHello } @@ -101,21 +104,13 @@ func ReadClientHello(data []byte, h *SniffHeader) error { return errNotClientHello } if nameType == 0 { - // QUIC separated across packets - // May cause the serverName to be incomplete - b := byte(0) - for _, b = range d[:nameLen] { - if b <= ' ' { - return protocol.ErrProtoNeedMoreData - } - } + serverName := string(d[:nameLen]) // An SNI value may not include a // trailing dot. See // https://tools.ietf.org/html/rfc6066#section-3. - if b == '.' { + if strings.HasSuffix(serverName, ".") { return errNotClientHello } - serverName := string(d[:nameLen]) h.domain = serverName return nil } diff --git a/common/protocol/tls/sniff_test.go b/common/protocol/tls/sniff_test.go index 59f3a35f..d334089c 100644 --- a/common/protocol/tls/sniff_test.go +++ b/common/protocol/tls/sniff_test.go @@ -147,7 +147,7 @@ func TestTLSHeaders(t *testing.T) { header, err := SniffTLS(test.input) if test.err { if err == nil { - t.Errorf("Expect error but nil in test %v", test) + t.Errorf("Exepct error but nil in test %v", test) } } else { if err != nil { diff --git a/common/protocol/user.go b/common/protocol/user.go index 75e8e654..8325f555 100644 --- a/common/protocol/user.go +++ b/common/protocol/user.go @@ -1,13 +1,8 @@ package protocol -import ( - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/serial" -) - func (u *User) GetTypedAccount() (Account, error) { if u.GetAccount() == nil { - return nil, errors.New("Account is missing").AtWarning() + return nil, newError("Account missing").AtWarning() } rawAccount, err := u.Account.GetInstance() @@ -20,7 +15,7 @@ func (u *User) GetTypedAccount() (Account, error) { if account, ok := rawAccount.(Account); ok { return account, nil } - return nil, errors.New("Unknown account type: ", u.Account.Type) + return nil, newError("Unknown account type: ", u.Account.Type) } func (u *User) ToMemoryUser() (*MemoryUser, error) { @@ -35,17 +30,6 @@ func (u *User) ToMemoryUser() (*MemoryUser, error) { }, nil } -func ToProtoUser(mu *MemoryUser) *User { - if mu == nil { - return nil - } - return &User{ - Account: serial.ToTypedMessage(mu.Account.ToProto()), - Email: mu.Email, - Level: mu.Level, - } -} - // MemoryUser is a parsed form of User, to reduce number of parsing of Account proto. type MemoryUser struct { // Account is the parsed account of the protocol. diff --git a/common/protocol/user.pb.go b/common/protocol/user.pb.go index df19fbd6..bc3b2bf8 100644 --- a/common/protocol/user.pb.go +++ b/common/protocol/user.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/protocol/user.proto package protocol @@ -21,7 +21,7 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// User is a generic user for all protocols. +// User is a generic user for all procotols. type User struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -36,9 +36,11 @@ type User struct { func (x *User) Reset() { *x = User{} - mi := &file_common_protocol_user_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_protocol_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *User) String() string { @@ -49,7 +51,7 @@ func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { mi := &file_common_protocol_user_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -122,7 +124,7 @@ func file_common_protocol_user_proto_rawDescGZIP() []byte { } var file_common_protocol_user_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_common_protocol_user_proto_goTypes = []any{ +var file_common_protocol_user_proto_goTypes = []interface{}{ (*User)(nil), // 0: xray.common.protocol.User (*serial.TypedMessage)(nil), // 1: xray.common.serial.TypedMessage } @@ -140,6 +142,20 @@ func file_common_protocol_user_proto_init() { if File_common_protocol_user_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_common_protocol_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/common/protocol/user.proto b/common/protocol/user.proto index 14cf995b..44770edf 100644 --- a/common/protocol/user.proto +++ b/common/protocol/user.proto @@ -8,7 +8,7 @@ option java_multiple_files = true; import "common/serial/typed_message.proto"; -// User is a generic user for all protocols. +// User is a generic user for all procotols. message User { uint32 level = 1; string email = 2; diff --git a/common/reflect/marshal.go b/common/reflect/marshal.go deleted file mode 100644 index 127dc8e0..00000000 --- a/common/reflect/marshal.go +++ /dev/null @@ -1,273 +0,0 @@ -package reflect - -import ( - "bytes" - "encoding/json" - "fmt" - "reflect" - "strings" - - cnet "github.com/xtls/xray-core/common/net" - cserial "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/infra/conf" -) - -func MarshalToJson(v interface{}, insertTypeInfo bool) (string, bool) { - if itf := marshalInterface(v, true, insertTypeInfo); itf != nil { - if b, err := JSONMarshalWithoutEscape(itf); err == nil { - return string(b[:]), true - } - } - return "", false -} - -func JSONMarshalWithoutEscape(t interface{}) ([]byte, error) { - buffer := &bytes.Buffer{} - encoder := json.NewEncoder(buffer) - encoder.SetIndent("", " ") - encoder.SetEscapeHTML(false) - err := encoder.Encode(t) - return buffer.Bytes(), err -} - -func marshalTypedMessage(v *cserial.TypedMessage, ignoreNullValue bool, insertTypeInfo bool) interface{} { - if v == nil { - return nil - } - tmsg, err := v.GetInstance() - if err != nil { - return nil - } - r := marshalInterface(tmsg, ignoreNullValue, insertTypeInfo) - if msg, ok := r.(map[string]interface{}); ok && insertTypeInfo { - msg["_TypedMessage_"] = v.Type - } - return r -} - -func marshalSlice(v reflect.Value, ignoreNullValue bool, insertTypeInfo bool) interface{} { - r := make([]interface{}, 0) - for i := 0; i < v.Len(); i++ { - rv := v.Index(i) - if rv.CanInterface() { - value := rv.Interface() - r = append(r, marshalInterface(value, ignoreNullValue, insertTypeInfo)) - } - } - return r -} - -func isNullValue(f reflect.StructField, rv reflect.Value) bool { - if rv.Kind() == reflect.Struct { - return false - } else if rv.Kind() == reflect.String && rv.Len() == 0 { - return true - } else if !isValueKind(rv.Kind()) && rv.IsNil() { - return true - } else if tag := f.Tag.Get("json"); strings.Contains(tag, "omitempty") { - if !rv.IsValid() || rv.IsZero() { - return true - } - } - return false -} - -func toJsonName(f reflect.StructField) string { - if tags := f.Tag.Get("protobuf"); len(tags) > 0 { - for _, tag := range strings.Split(tags, ",") { - if before, after, ok := strings.Cut(tag, "="); ok && before == "json" { - return after - } - } - } - if tag := f.Tag.Get("json"); len(tag) > 0 { - if before, _, ok := strings.Cut(tag, ","); ok { - return before - } else { - return tag - } - } - return f.Name -} - -func marshalStruct(v reflect.Value, ignoreNullValue bool, insertTypeInfo bool) interface{} { - r := make(map[string]interface{}) - t := v.Type() - for i := 0; i < v.NumField(); i++ { - rv := v.Field(i) - if rv.CanInterface() { - ft := t.Field(i) - if !ignoreNullValue || !isNullValue(ft, rv) { - name := toJsonName(ft) - value := rv.Interface() - tv := marshalInterface(value, ignoreNullValue, insertTypeInfo) - r[name] = tv - } - } - } - return r -} - -func marshalMap(v reflect.Value, ignoreNullValue bool, insertTypeInfo bool) interface{} { - // policy.level is map[uint32] *struct - kt := v.Type().Key() - vt := reflect.TypeOf((*interface{})(nil)) - mt := reflect.MapOf(kt, vt) - r := reflect.MakeMap(mt) - for _, key := range v.MapKeys() { - rv := v.MapIndex(key) - if rv.CanInterface() { - iv := rv.Interface() - tv := marshalInterface(iv, ignoreNullValue, insertTypeInfo) - if tv != nil || !ignoreNullValue { - r.SetMapIndex(key, reflect.ValueOf(&tv)) - } - } - } - return r.Interface() -} - -func marshalIString(v interface{}) (r string, ok bool) { - defer func() { - if err := recover(); err != nil { - r = "" - ok = false - } - }() - if iStringFn, ok := v.(interface{ String() string }); ok { - return iStringFn.String(), true - } - return "", false -} - -func serializePortList(portList *cnet.PortList) (interface{}, bool) { - if portList == nil { - return nil, false - } - - n := len(portList.Range) - if n == 1 { - if first := portList.Range[0]; first.From == first.To { - return first.From, true - } - } - - r := make([]string, 0, n) - for _, pr := range portList.Range { - if pr.From == pr.To { - r = append(r, pr.FromPort().String()) - } else { - r = append(r, fmt.Sprintf("%d-%d", pr.From, pr.To)) - } - } - return strings.Join(r, ","), true -} - -func marshalKnownType(v interface{}, ignoreNullValue bool, insertTypeInfo bool) (interface{}, bool) { - switch ty := v.(type) { - case cserial.TypedMessage: - return marshalTypedMessage(&ty, ignoreNullValue, insertTypeInfo), true - case *cserial.TypedMessage: - return marshalTypedMessage(ty, ignoreNullValue, insertTypeInfo), true - case map[string]json.RawMessage: - return ty, true - case []json.RawMessage: - return ty, true - case *json.RawMessage, json.RawMessage: - return ty, true - case *cnet.IPOrDomain: - if domain := v.(*cnet.IPOrDomain); domain != nil { - return domain.AsAddress().String(), true - } - return nil, false - case *cnet.PortList: - npl := v.(*cnet.PortList) - return serializePortList(npl) - case *conf.PortList: - cpl := v.(*conf.PortList) - return serializePortList(cpl.Build()) - case conf.Int32Range: - i32rng := v.(conf.Int32Range) - if i32rng.Left == i32rng.Right { - return i32rng.Left, true - } - return i32rng.String(), true - case cnet.Address: - if addr := v.(cnet.Address); addr != nil { - return addr.String(), true - } - return nil, false - default: - return nil, false - } -} - -func isValueKind(kind reflect.Kind) bool { - switch kind { - case reflect.Bool, - reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Int64, - reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32, - reflect.Uint64, - reflect.Uintptr, - reflect.Float32, - reflect.Float64, - reflect.Complex64, - reflect.Complex128, - reflect.String: - return true - default: - return false - } -} - -func marshalInterface(v interface{}, ignoreNullValue bool, insertTypeInfo bool) interface{} { - - if r, ok := marshalKnownType(v, ignoreNullValue, insertTypeInfo); ok { - return r - } - - rv := reflect.ValueOf(v) - if rv.Kind() == reflect.Ptr { - rv = rv.Elem() - } - k := rv.Kind() - if k == reflect.Invalid { - return nil - } - - if ty := rv.Type().Name(); isValueKind(k) { - if k.String() != ty { - if s, ok := marshalIString(v); ok { - return s - } - } - return v - } - - // fmt.Println("kind:", k, "type:", rv.Type().Name()) - - switch k { - case reflect.Struct: - return marshalStruct(rv, ignoreNullValue, insertTypeInfo) - case reflect.Slice: - return marshalSlice(rv, ignoreNullValue, insertTypeInfo) - case reflect.Array: - return marshalSlice(rv, ignoreNullValue, insertTypeInfo) - case reflect.Map: - return marshalMap(rv, ignoreNullValue, insertTypeInfo) - default: - break - } - - if str, ok := marshalIString(v); ok { - return str - } - return nil -} diff --git a/common/reflect/marshal_test.go b/common/reflect/marshal_test.go deleted file mode 100644 index 80df23f1..00000000 --- a/common/reflect/marshal_test.go +++ /dev/null @@ -1,244 +0,0 @@ -package reflect_test - -import ( - "bytes" - "encoding/json" - "strings" - "testing" - - "github.com/xtls/xray-core/common/protocol" - . "github.com/xtls/xray-core/common/reflect" - cserial "github.com/xtls/xray-core/common/serial" - iserial "github.com/xtls/xray-core/infra/conf/serial" - "github.com/xtls/xray-core/proxy/shadowsocks" -) - -func TestMashalAccount(t *testing.T) { - account := &shadowsocks.Account{ - Password: "shadowsocks-password", - CipherType: shadowsocks.CipherType_CHACHA20_POLY1305, - } - - user := &protocol.User{ - Level: 0, - Email: "love@v2ray.com", - Account: cserial.ToTypedMessage(account), - } - - j, ok := MarshalToJson(user, false) - if !ok || strings.Contains(j, "_TypedMessage_") { - - t.Error("marshal account failed") - } - - kws := []string{"CHACHA20_POLY1305", "cipherType", "shadowsocks-password"} - for _, kw := range kws { - if !strings.Contains(j, kw) { - t.Error("marshal account failed") - } - } - // t.Log(j) -} - -func TestMashalStruct(t *testing.T) { - type Foo = struct { - N int `json:"n"` - Np *int `json:"np"` - S string `json:"s"` - Arr *[]map[string]map[string]string `json:"arr"` - } - - n := 1 - np := &n - arr := make([]map[string]map[string]string, 0) - m1 := make(map[string]map[string]string, 0) - m2 := make(map[string]string, 0) - m2["hello"] = "world" - m1["foo"] = m2 - - arr = append(arr, m1) - - f1 := Foo{ - N: n, - Np: np, - S: "hello", - Arr: &arr, - } - - s, ok1 := MarshalToJson(f1, true) - sp, ok2 := MarshalToJson(&f1, true) - - if !ok1 || !ok2 || s != sp { - t.Error("marshal failed") - } - - f2 := Foo{} - if json.Unmarshal([]byte(s), &f2) != nil { - t.Error("json unmarshal failed") - } - - v := (*f2.Arr)[0]["foo"]["hello"] - - if f1.N != f2.N || *(f1.Np) != *(f2.Np) || f1.S != f2.S || v != "world" { - t.Error("f1 not equal to f2") - } -} - -func TestMarshalConfigJson(t *testing.T) { - - buf := bytes.NewBufferString(getConfig()) - config, err := iserial.DecodeJSONConfig(buf) - if err != nil { - t.Error("decode JSON config failed") - } - - bc, err := config.Build() - if err != nil { - t.Error("build core config failed") - } - - tmsg := cserial.ToTypedMessage(bc) - tc, ok := MarshalToJson(tmsg, true) - if !ok { - t.Error("marshal config failed") - } - - // t.Log(tc) - - keywords := []string{ - "4784f9b8-a879-4fec-9718-ebddefa47750", - "bing.com", - "inboundTag", - "level", - "stats", - "userDownlink", - "userUplink", - "system", - "inboundDownlink", - "outboundUplink", - "XHTTP_IN", - "\"host\": \"bing.com\"", - "scMaxEachPostBytes", - "\"from\": 100", - "\"to\": 1000", - "\"from\": 1000000", - "\"to\": 1000000", - } - for _, kw := range keywords { - if !strings.Contains(tc, kw) { - t.Log("config.json:", tc) - t.Error("keyword not found:", kw) - break - } - } -} - -func getConfig() string { - return `{ - "log": { - "loglevel": "debug" - }, - "stats": {}, - "policy": { - "levels": { - "0": { - "statsUserUplink": true, - "statsUserDownlink": true - } - }, - "system": { - "statsInboundUplink": true, - "statsInboundDownlink": true, - "statsOutboundUplink": true, - "statsOutboundDownlink": true - } - }, - "inbounds": [ - { - "tag": "agentin", - "protocol": "http", - "port": 18080, - "listen": "127.0.0.1", - "settings": {} - }, - { - "listen": "127.0.0.1", - "port": 10085, - "protocol": "dokodemo-door", - "settings": { - "address": "127.0.0.1" - }, - "tag": "api-in" - } - ], - "api": { - "tag": "api", - "services": [ - "HandlerService", - "StatsService" - ] - }, - "routing": { - "rules": [ - { - "inboundTag": [ - "api-in" - ], - "outboundTag": "api", - "type": "field" - } - ], - "domainStrategy": "AsIs" - }, - "outbounds": [ - { - "protocol": "vless", - "settings": { - "vnext": [ - { - "address": "1.2.3.4", - "port": 1234, - "users": [ - { - "id": "4784f9b8-a879-4fec-9718-ebddefa47750", - "encryption": "none" - } - ] - } - ] - }, - "tag": "XHTTP_IN", - "streamSettings": { - "network": "xhttp", - "xhttpSettings": { - "host": "bing.com", - "path": "/xhttp_client_upload", - "mode": "auto", - "extra": { - "noSSEHeader": false, - "scMaxEachPostBytes": 1000000, - "scMaxBufferedPosts": 30, - "xPaddingBytes": "100-1000" - } - }, - "sockopt": { - "tcpFastOpen": true, - "acceptProxyProtocol": false, - "tcpcongestion": "bbr", - "tcpMptcp": true - } - }, - "sniffing": { - "enabled": true, - "destOverride": [ - "http", - "tls", - "quic" - ], - "metadataOnly": false, - "routeOnly": true - } - } - ] -}` -} diff --git a/common/retry/errors.generated.go b/common/retry/errors.generated.go new file mode 100644 index 00000000..d9343213 --- /dev/null +++ b/common/retry/errors.generated.go @@ -0,0 +1,9 @@ +package retry + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/retry/retry.go b/common/retry/retry.go index 02dcbfb9..ec1ad696 100644 --- a/common/retry/retry.go +++ b/common/retry/retry.go @@ -1,12 +1,12 @@ package retry // import "github.com/xtls/xray-core/common/retry" +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "time" - - "github.com/xtls/xray-core/common/errors" ) -var ErrRetryFailed = errors.New("all retry attempts failed") +var ErrRetryFailed = newError("all retry attempts failed") // Strategy is a way to retry on a specific function. type Strategy interface { @@ -36,7 +36,7 @@ func (r *retryer) On(method func() error) error { time.Sleep(time.Duration(delay) * time.Millisecond) attempt++ } - return errors.New(accumulatedError).Base(ErrRetryFailed) + return newError(accumulatedError).Base(ErrRetryFailed) } // Timed returns a retry strategy with fixed interval. diff --git a/common/serial/typed_message.go b/common/serial/typed_message.go index baecc92e..e59d1d0d 100644 --- a/common/serial/typed_message.go +++ b/common/serial/typed_message.go @@ -1,9 +1,10 @@ package serial import ( - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" + "errors" + "reflect" + + "github.com/golang/protobuf/proto" ) // ToTypedMessage converts a proto Message into TypedMessage. @@ -20,17 +21,16 @@ func ToTypedMessage(message proto.Message) *TypedMessage { // GetMessageType returns the name of this proto Message. func GetMessageType(message proto.Message) string { - return string(message.ProtoReflect().Descriptor().FullName()) + return proto.MessageName(message) } // GetInstance creates a new instance of the message with messageType. func GetInstance(messageType string) (interface{}, error) { - messageTypeDescriptor := protoreflect.FullName(messageType) - mType, err := protoregistry.GlobalTypes.FindMessageByName(messageTypeDescriptor) - if err != nil { - return nil, err + mType := proto.MessageType(messageType) + if mType == nil || mType.Elem() == nil { + return nil, errors.New("Serial: Unknown type: " + messageType) } - return mType.New().Interface(), nil + return reflect.New(mType.Elem()).Interface(), nil } // GetInstance converts current TypedMessage into a proto Message. diff --git a/common/serial/typed_message.pb.go b/common/serial/typed_message.pb.go index b0466529..7bdbbf3a 100644 --- a/common/serial/typed_message.pb.go +++ b/common/serial/typed_message.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: common/serial/typed_message.proto package serial @@ -34,9 +34,11 @@ type TypedMessage struct { func (x *TypedMessage) Reset() { *x = TypedMessage{} - mi := &file_common_serial_typed_message_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_serial_typed_message_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *TypedMessage) String() string { @@ -47,7 +49,7 @@ func (*TypedMessage) ProtoMessage() {} func (x *TypedMessage) ProtoReflect() protoreflect.Message { mi := &file_common_serial_typed_message_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -108,7 +110,7 @@ func file_common_serial_typed_message_proto_rawDescGZIP() []byte { } var file_common_serial_typed_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_common_serial_typed_message_proto_goTypes = []any{ +var file_common_serial_typed_message_proto_goTypes = []interface{}{ (*TypedMessage)(nil), // 0: xray.common.serial.TypedMessage } var file_common_serial_typed_message_proto_depIdxs = []int32{ @@ -124,6 +126,20 @@ func file_common_serial_typed_message_proto_init() { if File_common_serial_typed_message_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_common_serial_typed_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TypedMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/common/session/context.go b/common/session/context.go index 0e2558c2..2959807e 100644 --- a/common/session/context.go +++ b/common/session/context.go @@ -2,31 +2,36 @@ package session import ( "context" - _ "unsafe" - "github.com/xtls/xray-core/common/ctx" - "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/routing" ) -//go:linkname IndependentCancelCtx context.newCancelCtx -func IndependentCancelCtx(parent context.Context) context.Context +type sessionKey int const ( - inboundSessionKey ctx.SessionKey = 1 - outboundSessionKey ctx.SessionKey = 2 - contentSessionKey ctx.SessionKey = 3 - muxPreferredSessionKey ctx.SessionKey = 4 - sockoptSessionKey ctx.SessionKey = 5 - trackedConnectionErrorKey ctx.SessionKey = 6 - dispatcherKey ctx.SessionKey = 7 - timeoutOnlyKey ctx.SessionKey = 8 - allowedNetworkKey ctx.SessionKey = 9 - handlerSessionKey ctx.SessionKey = 10 - mitmAlpn11Key ctx.SessionKey = 11 - mitmServerNameKey ctx.SessionKey = 12 + idSessionKey sessionKey = iota + inboundSessionKey + outboundSessionKey + contentSessionKey + muxPreferedSessionKey + sockoptSessionKey + trackedConnectionErrorKey + dispatcherKey ) +// ContextWithID returns a new context with the given ID. +func ContextWithID(ctx context.Context, id ID) context.Context { + return context.WithValue(ctx, idSessionKey, id) +} + +// IDFromContext returns ID in this context, or 0 if not contained. +func IDFromContext(ctx context.Context) ID { + if id, ok := ctx.Value(idSessionKey).(ID); ok { + return id + } + return 0 +} + func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context { return context.WithValue(ctx, inboundSessionKey, inbound) } @@ -38,37 +43,13 @@ func InboundFromContext(ctx context.Context) *Inbound { return nil } -func ContextWithOutbounds(ctx context.Context, outbounds []*Outbound) context.Context { - return context.WithValue(ctx, outboundSessionKey, outbounds) +func ContextWithOutbound(ctx context.Context, outbound *Outbound) context.Context { + return context.WithValue(ctx, outboundSessionKey, outbound) } -func ContextCloneOutboundsAndContent(ctx context.Context) context.Context { - outbounds := OutboundsFromContext(ctx) - newOutbounds := make([]*Outbound, len(outbounds)) - for i, ob := range outbounds { - if ob == nil { - continue - } - - // copy outbound by value - v := *ob - newOutbounds[i] = &v - } - - content := ContentFromContext(ctx) - newContent := Content{} - if content != nil { - newContent = *content - if content.Attributes != nil { - panic("content.Attributes != nil") - } - } - return ContextWithContent(ContextWithOutbounds(ctx, newOutbounds), &newContent) -} - -func OutboundsFromContext(ctx context.Context) []*Outbound { - if outbounds, ok := ctx.Value(outboundSessionKey).([]*Outbound); ok { - return outbounds +func OutboundFromContext(ctx context.Context) *Outbound { + if outbound, ok := ctx.Value(outboundSessionKey).(*Outbound); ok { + return outbound } return nil } @@ -84,14 +65,14 @@ func ContentFromContext(ctx context.Context) *Content { return nil } -// ContextWithMuxPreferred returns a new context with the given bool -func ContextWithMuxPreferred(ctx context.Context, forced bool) context.Context { - return context.WithValue(ctx, muxPreferredSessionKey, forced) +// ContextWithMuxPrefered returns a new context with the given bool +func ContextWithMuxPrefered(ctx context.Context, forced bool) context.Context { + return context.WithValue(ctx, muxPreferedSessionKey, forced) } -// MuxPreferredFromContext returns value in this context, or false if not contained. -func MuxPreferredFromContext(ctx context.Context) bool { - if val, ok := ctx.Value(muxPreferredSessionKey).(bool); ok { +// MuxPreferedFromContext returns value in this context, or false if not contained. +func MuxPreferedFromContext(ctx context.Context) bool { + if val, ok := ctx.Value(muxPreferedSessionKey).(bool); ok { return val } return false @@ -150,47 +131,3 @@ func DispatcherFromContext(ctx context.Context) routing.Dispatcher { } return nil } - -func ContextWithTimeoutOnly(ctx context.Context, only bool) context.Context { - return context.WithValue(ctx, timeoutOnlyKey, only) -} - -func TimeoutOnlyFromContext(ctx context.Context) bool { - if val, ok := ctx.Value(timeoutOnlyKey).(bool); ok { - return val - } - return false -} - -func ContextWithAllowedNetwork(ctx context.Context, network net.Network) context.Context { - return context.WithValue(ctx, allowedNetworkKey, network) -} - -func AllowedNetworkFromContext(ctx context.Context) net.Network { - if val, ok := ctx.Value(allowedNetworkKey).(net.Network); ok { - return val - } - return net.Network_Unknown -} - -func ContextWithMitmAlpn11(ctx context.Context, alpn11 bool) context.Context { - return context.WithValue(ctx, mitmAlpn11Key, alpn11) -} - -func MitmAlpn11FromContext(ctx context.Context) bool { - if val, ok := ctx.Value(mitmAlpn11Key).(bool); ok { - return val - } - return false -} - -func ContextWithMitmServerName(ctx context.Context, serverName string) context.Context { - return context.WithValue(ctx, mitmServerNameKey, serverName) -} - -func MitmServerNameFromContext(ctx context.Context) string { - if val, ok := ctx.Value(mitmServerNameKey).(string); ok { - return val - } - return "" -} diff --git a/common/session/session.go b/common/session/session.go index 4de97374..656a2404 100644 --- a/common/session/session.go +++ b/common/session/session.go @@ -5,18 +5,20 @@ import ( "context" "math/rand" - c "github.com/xtls/xray-core/common/ctx" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/signal" ) +// ID of a session. +type ID uint32 + // NewID generates a new ID. The generated ID is high likely to be unique, but not cryptographically secure. // The generated ID will never be 0. -func NewID() c.ID { +func NewID() ID { for { - id := c.ID(rand.Uint32()) + id := ID(rand.Uint32()) if id != 0 { return id } @@ -26,7 +28,7 @@ func NewID() c.ID { // ExportIDToError transfers session.ID into an error object, for logging purpose. // This can be used with error.WriteToLog(). func ExportIDToError(ctx context.Context) errors.ExportOption { - id := c.IDFromContext(ctx) + id := IDFromContext(ctx) return func(h *errors.ExportOptionHolder) { h.SessionID = uint32(id) } @@ -40,42 +42,27 @@ type Inbound struct { Gateway net.Destination // Tag of the inbound proxy that handles the connection. Tag string - // Name of the inbound proxy that handles the connection. - Name string - // User is the user that authenticates for the inbound. May be nil if the protocol allows anonymous traffic. + // User is the user that authencates for the inbound. May be nil if the protocol allows anounymous traffic. User *protocol.MemoryUser // Conn is actually internet.Connection. May be nil. Conn net.Conn // Timer of the inbound buf copier. May be nil. Timer *signal.ActivityTimer - // CanSpliceCopy is a property for this connection - // 1 = can, 2 = after processing protocol info should be able to, 3 = cannot - CanSpliceCopy int } // Outbound is the metadata of an outbound connection. type Outbound struct { // Target address of the outbound connection. - OriginalTarget net.Destination - Target net.Destination - RouteTarget net.Destination + Target net.Destination + RouteTarget net.Destination // Gateway address Gateway net.Address - // Tag of the outbound proxy that handles the connection. - Tag string - // Name of the outbound proxy that handles the connection. - Name string - // Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings - Conn net.Conn - // CanSpliceCopy is a property for this connection - // 1 = can, 2 = after processing protocol info should be able to, 3 = cannot - CanSpliceCopy int } // SniffingRequest controls the behavior of content sniffing. type SniffingRequest struct { - ExcludeForDomain []string // read-only once set - OverrideDestinationForProtocol []string // read-only once set + ExcludeForDomain []string + OverrideDestinationForProtocol []string Enabled bool MetadataOnly bool RouteOnly bool diff --git a/common/signal/timer.go b/common/signal/timer.go index ece9f496..a7f59cd9 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -67,9 +67,9 @@ func (t *ActivityTimer) SetTimeout(timeout time.Duration) { t.checkTask.Close() } t.checkTask = checkTask + t.Unlock() t.Update() common.Must(checkTask.Start()) - t.Unlock() } func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer { diff --git a/common/signal/timer_test.go b/common/signal/timer_test.go index 263bec9e..d56eed2b 100644 --- a/common/signal/timer_test.go +++ b/common/signal/timer_test.go @@ -29,7 +29,7 @@ func TestActivityTimerUpdate(t *testing.T) { timer.SetTimeout(time.Second * 1) time.Sleep(time.Second * 2) if ctx.Err() == nil { - t.Error("expected some error, but got nil") + t.Error("expcted some error, but got nil") } runtime.KeepAlive(timer) } diff --git a/common/singbridge/destination.go b/common/singbridge/destination.go deleted file mode 100644 index 98aed258..00000000 --- a/common/singbridge/destination.go +++ /dev/null @@ -1,52 +0,0 @@ -package singbridge - -import ( - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/xtls/xray-core/common/net" -) - -func ToNetwork(network string) net.Network { - switch N.NetworkName(network) { - case N.NetworkTCP: - return net.Network_TCP - case N.NetworkUDP: - return net.Network_UDP - default: - return net.Network_Unknown - } -} - -func ToDestination(socksaddr M.Socksaddr, network net.Network) net.Destination { - // IsFqdn() implicitly checks if the domain name is valid - if socksaddr.IsFqdn() { - return net.Destination{ - Network: network, - Address: net.DomainAddress(socksaddr.Fqdn), - Port: net.Port(socksaddr.Port), - } - } - - // IsIP() implicitly checks if the IP address is valid - if socksaddr.IsIP() { - return net.Destination{ - Network: network, - Address: net.IPAddress(socksaddr.Addr.AsSlice()), - Port: net.Port(socksaddr.Port), - } - } - - return net.Destination{} -} - -func ToSocksaddr(destination net.Destination) M.Socksaddr { - var addr M.Socksaddr - switch destination.Address.Family() { - case net.AddressFamilyDomain: - addr.Fqdn = destination.Address.Domain() - default: - addr.Addr = M.AddrFromIP(destination.Address.IP()) - } - addr.Port = uint16(destination.Port) - return addr -} diff --git a/common/singbridge/dialer.go b/common/singbridge/dialer.go deleted file mode 100644 index a6b32199..00000000 --- a/common/singbridge/dialer.go +++ /dev/null @@ -1,64 +0,0 @@ -package singbridge - -import ( - "context" - "os" - - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/net/cnc" - "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/proxy" - "github.com/xtls/xray-core/transport" - "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/pipe" -) - -var _ N.Dialer = (*XrayDialer)(nil) - -type XrayDialer struct { - internet.Dialer -} - -func NewDialer(dialer internet.Dialer) *XrayDialer { - return &XrayDialer{dialer} -} - -func (d *XrayDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - return d.Dialer.Dial(ctx, ToDestination(destination, ToNetwork(network))) -} - -func (d *XrayDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return nil, os.ErrInvalid -} - -type XrayOutboundDialer struct { - outbound proxy.Outbound - dialer internet.Dialer -} - -func NewOutboundDialer(outbound proxy.Outbound, dialer internet.Dialer) *XrayOutboundDialer { - return &XrayOutboundDialer{outbound, dialer} -} - -func (d *XrayOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - outbounds := session.OutboundsFromContext(ctx) - if len(outbounds) == 0 { - outbounds = []*session.Outbound{{}} - ctx = session.ContextWithOutbounds(ctx, outbounds) - } - ob := outbounds[len(outbounds)-1] - ob.Target = ToDestination(destination, ToNetwork(network)) - - opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)} - uplinkReader, uplinkWriter := pipe.New(opts...) - downlinkReader, downlinkWriter := pipe.New(opts...) - conn := cnc.NewConnection(cnc.ConnectionInputMulti(downlinkWriter), cnc.ConnectionOutputMulti(uplinkReader)) - go d.outbound.Process(ctx, &transport.Link{Reader: downlinkReader, Writer: uplinkWriter}, d.dialer) - return conn, nil -} - -func (d *XrayOutboundDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return nil, os.ErrInvalid -} diff --git a/common/singbridge/error.go b/common/singbridge/error.go deleted file mode 100644 index ac9e6351..00000000 --- a/common/singbridge/error.go +++ /dev/null @@ -1,10 +0,0 @@ -package singbridge - -import E "github.com/sagernet/sing/common/exceptions" - -func ReturnError(err error) error { - if E.IsClosedOrCanceled(err) { - return nil - } - return err -} diff --git a/common/singbridge/handler.go b/common/singbridge/handler.go deleted file mode 100644 index f200075c..00000000 --- a/common/singbridge/handler.go +++ /dev/null @@ -1,50 +0,0 @@ -package singbridge - -import ( - "context" - "io" - - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/transport" -) - -var ( - _ N.TCPConnectionHandler = (*Dispatcher)(nil) - _ N.UDPConnectionHandler = (*Dispatcher)(nil) -) - -type Dispatcher struct { - upstream routing.Dispatcher - newErrorFunc func(values ...any) *errors.Error -} - -func NewDispatcher(dispatcher routing.Dispatcher, newErrorFunc func(values ...any) *errors.Error) *Dispatcher { - return &Dispatcher{ - upstream: dispatcher, - newErrorFunc: newErrorFunc, - } -} - -func (d *Dispatcher) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { - xConn := NewConn(conn) - return d.upstream.DispatchLink(ctx, ToDestination(metadata.Destination, net.Network_TCP), &transport.Link{ - Reader: xConn, - Writer: xConn, - }) -} - -func (d *Dispatcher) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { - return d.upstream.DispatchLink(ctx, ToDestination(metadata.Destination, net.Network_UDP), &transport.Link{ - Reader: buf.NewPacketReader(conn.(io.Reader)), - Writer: buf.NewWriter(conn.(io.Writer)), - }) -} - -func (d *Dispatcher) NewError(ctx context.Context, err error) { - errors.LogInfo(ctx, err.Error()) -} diff --git a/common/singbridge/logger.go b/common/singbridge/logger.go deleted file mode 100644 index 16ff29cc..00000000 --- a/common/singbridge/logger.go +++ /dev/null @@ -1,70 +0,0 @@ -package singbridge - -import ( - "context" - - "github.com/sagernet/sing/common/logger" - "github.com/xtls/xray-core/common/errors" -) - -var _ logger.ContextLogger = (*XrayLogger)(nil) - -type XrayLogger struct { - newError func(values ...any) *errors.Error -} - -func NewLogger(newErrorFunc func(values ...any) *errors.Error) *XrayLogger { - return &XrayLogger{ - newErrorFunc, - } -} - -func (l *XrayLogger) Trace(args ...any) { -} - -func (l *XrayLogger) Debug(args ...any) { - errors.LogDebug(context.Background(), args...) -} - -func (l *XrayLogger) Info(args ...any) { - errors.LogInfo(context.Background(), args...) -} - -func (l *XrayLogger) Warn(args ...any) { - errors.LogWarning(context.Background(), args...) -} - -func (l *XrayLogger) Error(args ...any) { - errors.LogError(context.Background(), args...) -} - -func (l *XrayLogger) Fatal(args ...any) { -} - -func (l *XrayLogger) Panic(args ...any) { -} - -func (l *XrayLogger) TraceContext(ctx context.Context, args ...any) { -} - -func (l *XrayLogger) DebugContext(ctx context.Context, args ...any) { - errors.LogDebug(ctx, args...) -} - -func (l *XrayLogger) InfoContext(ctx context.Context, args ...any) { - errors.LogInfo(ctx, args...) -} - -func (l *XrayLogger) WarnContext(ctx context.Context, args ...any) { - errors.LogWarning(ctx, args...) -} - -func (l *XrayLogger) ErrorContext(ctx context.Context, args ...any) { - errors.LogError(ctx, args...) -} - -func (l *XrayLogger) FatalContext(ctx context.Context, args ...any) { -} - -func (l *XrayLogger) PanicContext(ctx context.Context, args ...any) { -} diff --git a/common/singbridge/packet.go b/common/singbridge/packet.go deleted file mode 100644 index fef955e7..00000000 --- a/common/singbridge/packet.go +++ /dev/null @@ -1,82 +0,0 @@ -package singbridge - -import ( - "context" - - B "github.com/sagernet/sing/common/buf" - "github.com/sagernet/sing/common/bufio" - M "github.com/sagernet/sing/common/metadata" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/transport" -) - -func CopyPacketConn(ctx context.Context, inboundConn net.Conn, link *transport.Link, destination net.Destination, serverConn net.PacketConn) error { - conn := &PacketConnWrapper{ - Reader: link.Reader, - Writer: link.Writer, - Dest: destination, - Conn: inboundConn, - } - return ReturnError(bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(serverConn))) -} - -type PacketConnWrapper struct { - buf.Reader - buf.Writer - net.Conn - Dest net.Destination - cached buf.MultiBuffer -} - -func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) { - if w.cached != nil { - mb, bb := buf.SplitFirst(w.cached) - if bb == nil { - w.cached = nil - } else { - buffer.Write(bb.Bytes()) - w.cached = mb - var destination net.Destination - if bb.UDP != nil { - destination = *bb.UDP - } else { - destination = w.Dest - } - bb.Release() - return ToSocksaddr(destination), nil - } - } - mb, err := w.ReadMultiBuffer() - if err != nil { - return M.Socksaddr{}, err - } - nb, bb := buf.SplitFirst(mb) - if bb == nil { - return M.Socksaddr{}, nil - } else { - buffer.Write(bb.Bytes()) - w.cached = nb - var destination net.Destination - if bb.UDP != nil { - destination = *bb.UDP - } else { - destination = w.Dest - } - bb.Release() - return ToSocksaddr(destination), nil - } -} - -func (w *PacketConnWrapper) WritePacket(buffer *B.Buffer, destination M.Socksaddr) error { - vBuf := buf.New() - vBuf.Write(buffer.Bytes()) - endpoint := ToDestination(destination, net.Network_UDP) - vBuf.UDP = &endpoint - return w.Writer.WriteMultiBuffer(buf.MultiBuffer{vBuf}) -} - -func (w *PacketConnWrapper) Close() error { - buf.ReleaseMulti(w.cached) - return nil -} diff --git a/common/singbridge/pipe.go b/common/singbridge/pipe.go deleted file mode 100644 index d04ebda4..00000000 --- a/common/singbridge/pipe.go +++ /dev/null @@ -1,61 +0,0 @@ -package singbridge - -import ( - "context" - "io" - "net" - - "github.com/sagernet/sing/common/bufio" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/transport" -) - -func CopyConn(ctx context.Context, inboundConn net.Conn, link *transport.Link, serverConn net.Conn) error { - conn := &PipeConnWrapper{ - W: link.Writer, - Conn: inboundConn, - } - if ir, ok := link.Reader.(io.Reader); ok { - conn.R = ir - } else { - conn.R = &buf.BufferedReader{Reader: link.Reader} - } - return ReturnError(bufio.CopyConn(ctx, conn, serverConn)) -} - -type PipeConnWrapper struct { - R io.Reader - W buf.Writer - net.Conn -} - -func (w *PipeConnWrapper) Close() error { - return nil -} - -func (w *PipeConnWrapper) Read(b []byte) (n int, err error) { - return w.R.Read(b) -} - -func (w *PipeConnWrapper) Write(p []byte) (n int, err error) { - n = len(p) - var mb buf.MultiBuffer - pLen := len(p) - for pLen > 0 { - buffer := buf.New() - if pLen > buf.Size { - _, err = buffer.Write(p[:buf.Size]) - p = p[buf.Size:] - } else { - buffer.Write(p) - } - pLen -= int(buffer.Len()) - mb = append(mb, buffer) - } - err = w.W.WriteMultiBuffer(mb) - if err != nil { - n = 0 - buf.ReleaseMulti(mb) - } - return -} diff --git a/common/singbridge/reader.go b/common/singbridge/reader.go deleted file mode 100644 index 1ace1845..00000000 --- a/common/singbridge/reader.go +++ /dev/null @@ -1,66 +0,0 @@ -package singbridge - -import ( - "time" - - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/bufio" - N "github.com/sagernet/sing/common/network" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/net" -) - -var ( - _ buf.Reader = (*Conn)(nil) - _ buf.TimeoutReader = (*Conn)(nil) - _ buf.Writer = (*Conn)(nil) -) - -type Conn struct { - net.Conn - writer N.VectorisedWriter -} - -func NewConn(conn net.Conn) *Conn { - writer, _ := bufio.CreateVectorisedWriter(conn) - return &Conn{ - Conn: conn, - writer: writer, - } -} - -func (c *Conn) ReadMultiBuffer() (buf.MultiBuffer, error) { - buffer, err := buf.ReadBuffer(c.Conn) - if err != nil { - return nil, err - } - return buf.MultiBuffer{buffer}, nil -} - -func (c *Conn) ReadMultiBufferTimeout(duration time.Duration) (buf.MultiBuffer, error) { - err := c.SetReadDeadline(time.Now().Add(duration)) - if err != nil { - return nil, err - } - defer c.SetReadDeadline(time.Time{}) - return c.ReadMultiBuffer() -} - -func (c *Conn) WriteMultiBuffer(bufferList buf.MultiBuffer) error { - defer buf.ReleaseMulti(bufferList) - if c.writer != nil { - bytesList := make([][]byte, len(bufferList)) - for i, buffer := range bufferList { - bytesList[i] = buffer.Bytes() - } - return common.Error(bufio.WriteVectorised(c.writer, bytesList)) - } - // Since this conn is only used by tun, we don't force buffer writes to merge. - for _, buffer := range bufferList { - _, err := c.Conn.Write(buffer.Bytes()) - if err != nil { - return err - } - } - return nil -} diff --git a/common/strmatcher/ac_automaton_matcher.go b/common/strmatcher/ac_automaton_matcher.go index 24be9dac..ab7c09bd 100644 --- a/common/strmatcher/ac_automaton_matcher.go +++ b/common/strmatcher/ac_automaton_matcher.go @@ -225,11 +225,7 @@ func (ac *ACAutomaton) Match(s string) bool { // 2. the match string is through a fail edge. NOT FULL MATCH // 2.1 Through a fail edge, but there exists a valid node. SUBSTR for i := len(s) - 1; i >= 0; i-- { - chr := int(s[i]) - if chr >= len(char2Index) { - return false - } - idx := char2Index[chr] + idx := char2Index[s[i]] fullMatch = fullMatch && ac.trie[node][idx].edgeType node = ac.trie[node][idx].nextNode switch ac.exists[node].matchType { diff --git a/common/strmatcher/strmatcher_test.go b/common/strmatcher/strmatcher_test.go index 408ae628..2e48c1b7 100644 --- a/common/strmatcher/strmatcher_test.go +++ b/common/strmatcher/strmatcher_test.go @@ -217,10 +217,6 @@ func TestACAutomaton(t *testing.T) { pattern: "vvgoogle.com", res: true, }, - { - pattern: "½", - res: false, - }, } for _, test := range cases2Output { if m := ac.Match(test.pattern); m != test.res { @@ -228,6 +224,7 @@ func TestACAutomaton(t *testing.T) { } } } + { cases3Input := []struct { pattern string diff --git a/common/task/task.go b/common/task/task.go index eeba1dcd..52b0d44b 100644 --- a/common/task/task.go +++ b/common/task/task.go @@ -38,12 +38,6 @@ func Run(ctx context.Context, tasks ...func() error) error { }(task) } - /* - if altctx := ctx.Value("altctx"); altctx != nil { - ctx = altctx.(context.Context) - } - */ - for i := 0; i < n; i++ { select { case err := <-done: @@ -54,11 +48,5 @@ func Run(ctx context.Context, tasks ...func() error) error { } } - /* - if cancel := ctx.Value("cancel"); cancel != nil { - cancel.(context.CancelFunc)() - } - */ - return nil } diff --git a/common/type.go b/common/type.go index 8ee8745c..1f85b4ef 100644 --- a/common/type.go +++ b/common/type.go @@ -3,8 +3,6 @@ package common import ( "context" "reflect" - - "github.com/xtls/xray-core/common/errors" ) // ConfigCreator is a function to create an object by a config. @@ -16,7 +14,7 @@ var typeCreatorRegistry = make(map[reflect.Type]ConfigCreator) func RegisterConfig(config interface{}, configCreator ConfigCreator) error { configType := reflect.TypeOf(config) if _, found := typeCreatorRegistry[configType]; found { - return errors.New(configType.Name() + " is already registered").AtError() + return newError(configType.Name() + " is already registered").AtError() } typeCreatorRegistry[configType] = configCreator return nil @@ -27,7 +25,7 @@ func CreateObject(ctx context.Context, config interface{}) (interface{}, error) configType := reflect.TypeOf(config) creator, found := typeCreatorRegistry[configType] if !found { - return nil, errors.New(configType.String() + " is not registered").AtError() + return nil, newError(configType.String() + " is not registered").AtError() } return creator(ctx, config) } diff --git a/common/xudp/xudp.go b/common/xudp/xudp.go index 1dfff16a..80a35e41 100644 --- a/common/xudp/xudp.go +++ b/common/xudp/xudp.go @@ -1,80 +1,30 @@ package xudp import ( - "context" - "crypto/rand" - "encoding/base64" - "fmt" "io" - "strconv" - "strings" - "time" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/session" - "lukechampine.com/blake3" ) -var AddrParser = protocol.NewAddressParser( +var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), protocol.PortThenAddress(), ) -var ( - Show bool - BaseKey []byte -) - -func init() { - if strings.ToLower(platform.NewEnvFlag(platform.XUDPLog).GetValue(func() string { return "" })) == "true" { - Show = true - } - rand.Read(BaseKey) - go func() { - time.Sleep(100 * time.Millisecond) // this is not nice, but need to give some time for Android to setup ENV - if raw := platform.NewEnvFlag(platform.XUDPBaseKey).GetValue(func() string { return "" }); raw != "" { - if BaseKey, _ = base64.RawURLEncoding.DecodeString(raw); len(BaseKey) == 32 { - return - } - panic(platform.XUDPBaseKey + ": invalid value (BaseKey must be 32 bytes): " + raw + " len " + strconv.Itoa(len(BaseKey))) - } - }() -} - -func GetGlobalID(ctx context.Context) (globalID [8]byte) { - if cone := ctx.Value("cone"); cone == nil || !cone.(bool) { // cone is nil only in some unit tests - return - } - if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.Network == net.Network_UDP && - (inbound.Name == "dokodemo-door" || inbound.Name == "socks" || inbound.Name == "shadowsocks") { - h := blake3.New(8, BaseKey) - h.Write([]byte(inbound.Source.String())) - copy(globalID[:], h.Sum(nil)) - if Show { - errors.LogInfo(ctx, fmt.Sprintf("XUDP inbound.Source.String(): %v\tglobalID: %v\n", inbound.Source.String(), globalID)) - } - } - return -} - -func NewPacketWriter(writer buf.Writer, dest net.Destination, globalID [8]byte) *PacketWriter { +func NewPacketWriter(writer buf.Writer, dest net.Destination) *PacketWriter { return &PacketWriter{ - Writer: writer, - Dest: dest, - GlobalID: globalID, + Writer: writer, + Dest: dest, } } type PacketWriter struct { - Writer buf.Writer - Dest net.Destination - GlobalID [8]byte + Writer buf.Writer + Dest net.Destination } func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { @@ -87,22 +37,19 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { } eb := buf.New() - eb.Write([]byte{0, 0, 0, 0}) // Meta data length; Mux Session ID + eb.Write([]byte{0, 0, 0, 0}) if w.Dest.Network == net.Network_UDP { eb.WriteByte(1) // New eb.WriteByte(1) // Opt eb.WriteByte(2) // UDP - AddrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port) - if b.UDP != nil { // make sure it's user's proxy request - eb.Write(w.GlobalID[:]) // no need to check whether it's empty - } + addrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port) w.Dest.Network = net.Network_Unknown } else { eb.WriteByte(2) // Keep - eb.WriteByte(1) // Opt + eb.WriteByte(1) if b.UDP != nil { - eb.WriteByte(2) // UDP - AddrParser.WriteAddressPort(eb, b.UDP.Address, b.UDP.Port) + eb.WriteByte(2) + addrParser.WriteAddressPort(eb, b.UDP.Address, b.UDP.Port) } } l := eb.Len() - 2 @@ -149,10 +96,9 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { discard := false switch b.Byte(2) { case 2: - if l > 4 && b.Byte(4) == 2 { // MUST check the flag first + if l != 4 { b.Advance(5) - // b.Clear() will be called automatically if all data had been read. - addr, port, err := AddrParser.ReadAddressPort(nil, b) + addr, port, err := addrParser.ReadAddressPort(nil, b) if err != nil { b.Release() return nil, err @@ -169,7 +115,6 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { b.Release() return nil, io.EOF } - b.Clear() // in case there is padding (empty bytes) attached if b.Byte(3) == 1 { if _, err := io.ReadFull(r.Reader, r.cache); err != nil { b.Release() @@ -177,6 +122,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { } length := int32(r.cache[0])<<8 | int32(r.cache[1]) if length > 0 { + b.Clear() if _, err := b.ReadFullFrom(r.Reader, length); err != nil { b.Release() return nil, err diff --git a/common/xudp/xudp_test.go b/common/xudp/xudp_test.go deleted file mode 100644 index 78ddfa27..00000000 --- a/common/xudp/xudp_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package xudp - -import ( - "testing" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/net" -) - -func TestXudpReadWrite(t *testing.T) { - addr, _ := net.ParseDestination("tcp:127.0.0.1:1345") - mb := make(buf.MultiBuffer, 0, 16) - m := buf.MultiBufferContainer{ - MultiBuffer: mb, - } - var arr [8]byte - writer := NewPacketWriter(&m, addr, arr) - - source := make(buf.MultiBuffer, 0, 16) - b := buf.New() - b.WriteByte('a') - b.UDP = &addr - source = append(source, b) - writer.WriteMultiBuffer(source) - - reader := NewPacketReader(&m) - dest, err := reader.ReadMultiBuffer() - common.Must(err) - if dest[0].Byte(0) != 'a' { - t.Error("failed to parse xudp buffer") - } - if dest[0].UDP.Port != 1345 { - t.Error("failed to parse xudp buffer") - } -} diff --git a/core/config.go b/core/config.go index ec9e5aa4..5892226b 100644 --- a/core/config.go +++ b/core/config.go @@ -2,15 +2,13 @@ package core import ( "io" - "slices" "strings" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/cmdarg" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/main/confloader" - "google.golang.org/protobuf/proto" ) // ConfigFormat is a configurable format of Xray config file. @@ -20,39 +18,30 @@ type ConfigFormat struct { Loader ConfigLoader } -type ConfigSource struct { - Name string - Format string -} - // ConfigLoader is a utility to load Xray config from external source. type ConfigLoader func(input interface{}) (*Config, error) // ConfigBuilder is a builder to build core.Config from filenames and formats -type ConfigBuilder func(files []*ConfigSource) (*Config, error) - -// ConfigsMerger merges multiple json configs into a single one -type ConfigsMerger func(files []*ConfigSource) (string, error) +type ConfigBuilder func(files []string, formats []string) (*Config, error) var ( configLoaderByName = make(map[string]*ConfigFormat) configLoaderByExt = make(map[string]*ConfigFormat) ConfigBuilderForFiles ConfigBuilder - ConfigMergedFormFiles ConfigsMerger ) // RegisterConfigLoader add a new ConfigLoader. func RegisterConfigLoader(format *ConfigFormat) error { name := strings.ToLower(format.Name) if _, found := configLoaderByName[name]; found { - return errors.New(format.Name, " already registered.") + return newError(format.Name, " already registered.") } configLoaderByName[name] = format for _, ext := range format.Extension { lext := strings.ToLower(ext) if f, found := configLoaderByExt[lext]; found { - return errors.New(ext, " already registered to ", f.Name) + return newError(ext, " already registered to ", f.Name) } configLoaderByExt[lext] = format } @@ -60,21 +49,6 @@ func RegisterConfigLoader(format *ConfigFormat) error { return nil } -func GetMergedConfig(args cmdarg.Arg) (string, error) { - var files []*ConfigSource - supported := []string{"json", "yaml", "toml"} - for _, file := range args { - format := getFormat(file) - if slices.Contains(supported, format) { - files = append(files, &ConfigSource{ - Name: file, - Format: format, - }) - } - } - return ConfigMergedFormFiles(files) -} - func GetFormatByExtension(ext string) string { switch strings.ToLower(ext) { case "pb", "protobuf": @@ -83,7 +57,7 @@ func GetFormatByExtension(ext string) string { return "yaml" case "toml": return "toml" - case "json", "jsonc": + case "json": return "json" default: return "" @@ -105,7 +79,7 @@ func getFormat(filename string) string { func LoadConfig(formatName string, input interface{}) (*Config, error) { switch v := input.(type) { case cmdarg.Arg: - files := make([]*ConfigSource, len(v)) + formats := make([]string, len(v)) hasProtobuf := false for i, file := range v { var f string @@ -121,16 +95,13 @@ func LoadConfig(formatName string, input interface{}) (*Config, error) { } if f == "" { - return nil, errors.New("Failed to get format of ", file).AtWarning() + return nil, newError("Failed to get format of ", file).AtWarning() } if f == "protobuf" { hasProtobuf = true } - files[i] = &ConfigSource{ - Name: file, - Format: f, - } + formats[i] = f } // only one protobuf config file is allowed @@ -138,21 +109,22 @@ func LoadConfig(formatName string, input interface{}) (*Config, error) { if len(v) == 1 { return configLoaderByName["protobuf"].Loader(v) } else { - return nil, errors.New("Only one protobuf config file is allowed").AtWarning() + return nil, newError("Only one protobuf config file is allowed").AtWarning() } } // to avoid import cycle - return ConfigBuilderForFiles(files) + return ConfigBuilderForFiles(v, formats) + case io.Reader: if f, found := configLoaderByName[formatName]; found { return f.Loader(v) } else { - return nil, errors.New("Unable to load config in", formatName).AtWarning() + return nil, newError("Unable to load config in", formatName).AtWarning() } } - return nil, errors.New("Unable to load config").AtWarning() + return nil, newError("Unable to load config").AtWarning() } func loadProtobufConfig(data []byte) (*Config, error) { @@ -180,7 +152,7 @@ func init() { common.Must(err) return loadProtobufConfig(data) default: - return nil, errors.New("unknown type") + return nil, newError("unknow type") } }, })) diff --git a/core/config.pb.go b/core/config.pb.go index d80cc4d4..2364674d 100644 --- a/core/config.pb.go +++ b/core/config.pb.go @@ -1,13 +1,14 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: core/config.proto package core import ( serial "github.com/xtls/xray-core/common/serial" + global "github.com/xtls/xray-core/transport/global" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -37,6 +38,12 @@ type Config struct { // implement the Feature interface, and its config type must be registered // through common.RegisterConfig. App []*serial.TypedMessage `protobuf:"bytes,4,rep,name=app,proto3" json:"app,omitempty"` + // Transport settings. + // Deprecated. Each inbound and outbound should choose their own transport + // config. Date to remove: 2020-01-13 + // + // Deprecated: Do not use. + Transport *global.Config `protobuf:"bytes,5,opt,name=transport,proto3" json:"transport,omitempty"` // Configuration for extensions. The config may not work if corresponding // extension is not loaded into Xray. Xray will ignore such config during // initialization. @@ -45,9 +52,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_core_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_core_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -58,7 +67,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_core_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -94,6 +103,14 @@ func (x *Config) GetApp() []*serial.TypedMessage { return nil } +// Deprecated: Do not use. +func (x *Config) GetTransport() *global.Config { + if x != nil { + return x.Transport + } + return nil +} + func (x *Config) GetExtension() []*serial.TypedMessage { if x != nil { return x.Extension @@ -118,9 +135,11 @@ type InboundHandlerConfig struct { func (x *InboundHandlerConfig) Reset() { *x = InboundHandlerConfig{} - mi := &file_core_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_core_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *InboundHandlerConfig) String() string { @@ -131,7 +150,7 @@ func (*InboundHandlerConfig) ProtoMessage() {} func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message { mi := &file_core_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -187,9 +206,11 @@ type OutboundHandlerConfig struct { func (x *OutboundHandlerConfig) Reset() { *x = OutboundHandlerConfig{} - mi := &file_core_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_core_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *OutboundHandlerConfig) String() string { @@ -200,7 +221,7 @@ func (*OutboundHandlerConfig) ProtoMessage() {} func (x *OutboundHandlerConfig) ProtoReflect() protoreflect.Message { mi := &file_core_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -257,54 +278,60 @@ var file_core_config_proto_rawDesc = []byte{ 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0xfb, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x07, - 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, - 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x18, 0x02, 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, 0x08, 0x6f, 0x75, 0x74, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 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, 0x03, 0x61, 0x70, 0x70, 0x12, 0x3e, 0x0a, 0x09, 0x65, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 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, 0x09, - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 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, + 0x6f, 0x1a, 0x1d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0xb5, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x07, 0x69, + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x69, + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x18, 0x02, 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, 0x08, 0x6f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 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, 0x03, 0x61, 0x70, 0x70, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, + 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 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, 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, 0xef, 0x01, 0x0a, 0x15, 0x4f, 0x75, 0x74, 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, 0x49, - 0x0a, 0x0f, 0x73, 0x65, 0x6e, 0x64, 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, 0x0e, 0x73, 0x65, 0x6e, 0x64, 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, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, - 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x3d, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x50, 0x01, 0x5a, 0x1e, 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, 0x63, 0x6f, 0x72, 0x65, 0xaa, 0x02, 0x09, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, - 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 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, 0xef, 0x01, 0x0a, 0x15, + 0x4f, 0x75, 0x74, 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, 0x49, 0x0a, 0x0f, 0x73, 0x65, 0x6e, 0x64, 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, 0x0e, 0x73, 0x65, 0x6e, 0x64, 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, 0x12, 0x16, 0x0a, 0x06, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x3d, 0x0a, + 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x50, 0x01, + 0x5a, 0x1e, 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, 0x63, 0x6f, 0x72, 0x65, + 0xaa, 0x02, 0x09, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -320,26 +347,28 @@ func file_core_config_proto_rawDescGZIP() []byte { } var file_core_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_core_config_proto_goTypes = []any{ +var file_core_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.core.Config (*InboundHandlerConfig)(nil), // 1: xray.core.InboundHandlerConfig (*OutboundHandlerConfig)(nil), // 2: xray.core.OutboundHandlerConfig (*serial.TypedMessage)(nil), // 3: xray.common.serial.TypedMessage + (*global.Config)(nil), // 4: xray.transport.Config } var file_core_config_proto_depIdxs = []int32{ 1, // 0: xray.core.Config.inbound:type_name -> xray.core.InboundHandlerConfig 2, // 1: xray.core.Config.outbound:type_name -> xray.core.OutboundHandlerConfig 3, // 2: xray.core.Config.app:type_name -> xray.common.serial.TypedMessage - 3, // 3: xray.core.Config.extension:type_name -> xray.common.serial.TypedMessage - 3, // 4: xray.core.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage - 3, // 5: xray.core.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage - 3, // 6: xray.core.OutboundHandlerConfig.sender_settings:type_name -> xray.common.serial.TypedMessage - 3, // 7: xray.core.OutboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 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 + 4, // 3: xray.core.Config.transport:type_name -> xray.transport.Config + 3, // 4: xray.core.Config.extension:type_name -> xray.common.serial.TypedMessage + 3, // 5: xray.core.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage + 3, // 6: xray.core.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage + 3, // 7: xray.core.OutboundHandlerConfig.sender_settings:type_name -> xray.common.serial.TypedMessage + 3, // 8: xray.core.OutboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_core_config_proto_init() } @@ -347,6 +376,44 @@ func file_core_config_proto_init() { if File_core_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_core_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InboundHandlerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OutboundHandlerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/core/config.proto b/core/config.proto index 32595d6d..821633a8 100644 --- a/core/config.proto +++ b/core/config.proto @@ -7,6 +7,7 @@ option java_package = "com.xray.core"; option java_multiple_files = true; import "common/serial/typed_message.proto"; +import "transport/global/config.proto"; // Config is the master config of Xray. Xray takes this config as input and // functions accordingly. @@ -25,6 +26,11 @@ message Config { // through common.RegisterConfig. repeated xray.common.serial.TypedMessage app = 4; + // Transport settings. + // Deprecated. Each inbound and outbound should choose their own transport + // config. Date to remove: 2020-01-13 + xray.transport.Config transport = 5 [deprecated = true]; + // Configuration for extensions. The config may not work if corresponding // extension is not loaded into Xray. Xray will ignore such config during // initialization. diff --git a/core/core.go b/core/core.go index 99ed573e..05148110 100644 --- a/core/core.go +++ b/core/core.go @@ -9,6 +9,8 @@ // connections. package core +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "fmt" "runtime" @@ -17,9 +19,9 @@ import ( ) var ( - Version_x byte = 25 - Version_y byte = 6 - Version_z byte = 8 + Version_x byte = 1 + Version_y byte = 8 + Version_z byte = 0 ) var ( diff --git a/core/errors.generated.go b/core/errors.generated.go new file mode 100644 index 00000000..47636b9c --- /dev/null +++ b/core/errors.generated.go @@ -0,0 +1,9 @@ +package core + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/core/functions.go b/core/functions.go index 09c4b5dd..7885fd14 100644 --- a/core/functions.go +++ b/core/functions.go @@ -5,7 +5,6 @@ import ( "context" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/features/routing" @@ -51,7 +50,7 @@ func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, err dispatcher := v.GetFeature(routing.DispatcherType()) if dispatcher == nil { - return nil, errors.New("routing.Dispatcher is not registered in Xray core") + return nil, newError("routing.Dispatcher is not registered in Xray core") } r, err := dispatcher.(routing.Dispatcher).Dispatch(ctx, dest) @@ -78,7 +77,7 @@ func DialUDP(ctx context.Context, v *Instance) (net.PacketConn, error) { dispatcher := v.GetFeature(routing.DispatcherType()) if dispatcher == nil { - return nil, errors.New("routing.Dispatcher is not registered in Xray core") + return nil, newError("routing.Dispatcher is not registered in Xray core") } return udp.DialDispatcher(ctx, dispatcher.(routing.Dispatcher)) } diff --git a/core/functions_test.go b/core/functions_test.go index 5658de1c..2355cc03 100644 --- a/core/functions_test.go +++ b/core/functions_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/golang/protobuf/proto" "github.com/google/go-cmp/cmp" "github.com/xtls/xray-core/app/dispatcher" "github.com/xtls/xray-core/app/proxyman" @@ -17,7 +18,6 @@ import ( "github.com/xtls/xray-core/proxy/freedom" "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/testing/servers/udp" - "google.golang.org/protobuf/proto" ) func xor(b []byte) []byte { diff --git a/core/xray.go b/core/xray.go index 58135c96..5c7518f9 100644 --- a/core/xray.go +++ b/core/xray.go @@ -2,12 +2,11 @@ package core import ( "context" + "os" "reflect" "sync" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/dns" @@ -44,13 +43,22 @@ func getFeature(allFeatures []features.Feature, t reflect.Type) features.Feature return nil } -func (r *resolution) callbackResolution(allFeatures []features.Feature) error { +func (r *resolution) resolve(allFeatures []features.Feature) (bool, error) { + var fs []features.Feature + for _, d := range r.deps { + f := getFeature(allFeatures, d) + if f == nil { + return false, nil + } + fs = append(fs, f) + } + callback := reflect.ValueOf(r.callback) var input []reflect.Value callbackType := callback.Type() for i := 0; i < callbackType.NumIn(); i++ { pt := callbackType.In(i) - for _, f := range allFeatures { + for _, f := range fs { if reflect.TypeOf(f).AssignableTo(pt) { input = append(input, reflect.ValueOf(f)) break @@ -75,26 +83,19 @@ func (r *resolution) callbackResolution(allFeatures []features.Feature) error { } } - return err + return true, err } -// Instance combines all Xray features. +// Instance combines all functionalities in Xray. type Instance struct { - statusLock sync.Mutex - features []features.Feature - pendingResolutions []resolution - pendingOptionalResolutions []resolution - running bool - resolveLock sync.Mutex + access sync.Mutex + features []features.Feature + featureResolutions []resolution + running bool ctx context.Context } -// Instance state -func (server *Instance) IsRunning() bool { - return server.running -} - func AddInboundHandler(server *Instance, config *InboundHandlerConfig) error { inboundManager := server.GetFeature(inbound.ManagerType()).(inbound.Manager) rawHandler, err := CreateObject(server, config) @@ -103,7 +104,7 @@ func AddInboundHandler(server *Instance, config *InboundHandlerConfig) error { } handler, ok := rawHandler.(inbound.Handler) if !ok { - return errors.New("not an InboundHandler") + return newError("not an InboundHandler") } if err := inboundManager.AddHandler(server.ctx, handler); err != nil { return err @@ -129,7 +130,7 @@ func AddOutboundHandler(server *Instance, config *OutboundHandlerConfig) error { } handler, ok := rawHandler.(outbound.Handler) if !ok { - return errors.New("not an OutboundHandler") + return newError("not an OutboundHandler") } if err := outboundManager.AddHandler(server.ctx, handler); err != nil { return err @@ -151,14 +152,7 @@ func addOutboundHandlers(server *Instance, configs []*OutboundHandlerConfig) err // See Instance.RequireFeatures for more information. func RequireFeatures(ctx context.Context, callback interface{}) error { v := MustFromContext(ctx) - return v.RequireFeatures(callback, false) -} - -// OptionalFeatures is a helper function to aquire features from Instance in context. -// See Instance.RequireFeatures for more information. -func OptionalFeatures(ctx context.Context, callback interface{}) error { - v := MustFromContext(ctx) - return v.RequireFeatures(callback, true) + return v.RequireFeatures(callback) } // New returns a new Xray instance based on given configuration. @@ -187,8 +181,14 @@ func NewWithContext(ctx context.Context, config *Config) (*Instance, error) { } func initInstanceWithConfig(config *Config, server *Instance) (bool, error) { - server.ctx = context.WithValue(server.ctx, "cone", - platform.NewEnvFlag(platform.UseCone).GetValue(func() string { return "" }) != "true") + server.ctx = context.WithValue(server.ctx, "cone", os.Getenv("XRAY_CONE_DISABLED") != "true") + + if config.Transport != nil { + features.PrintDeprecatedFeatureWarning("global transport settings") + } + if err := config.Transport.Apply(); err != nil { + return true, err + } for _, appSettings := range config.App { settings, err := appSettings.GetInstance() @@ -232,12 +232,9 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) { }(), ) - server.resolveLock.Lock() - if server.pendingResolutions != nil { - server.resolveLock.Unlock() - return true, errors.New("not all dependencies are resolved.") + if server.featureResolutions != nil { + return true, newError("not all dependency are resolved.") } - server.resolveLock.Unlock() if err := addInboundHandlers(server, config.Inbound); err != nil { return true, err @@ -256,19 +253,19 @@ func (s *Instance) Type() interface{} { // Close shutdown the Xray instance. func (s *Instance) Close() error { - s.statusLock.Lock() - defer s.statusLock.Unlock() + s.access.Lock() + defer s.access.Unlock() s.running = false - var errs []interface{} + var errors []interface{} for _, f := range s.features { if err := f.Close(); err != nil { - errs = append(errs, err) + errors = append(errors, err) } } - if len(errs) > 0 { - return errors.New("failed to close all features").Base(errors.New(serial.Concat(errs...))) + if len(errors) > 0 { + return newError("failed to close all features").Base(newError(serial.Concat(errors...))) } return nil @@ -276,7 +273,7 @@ func (s *Instance) Close() error { // RequireFeatures registers a callback, which will be called when all dependent features are registered. // The callback must be a func(). All its parameters must be features.Feature. -func (s *Instance) RequireFeatures(callback interface{}, optional bool) error { +func (s *Instance) RequireFeatures(callback interface{}) error { callbackType := reflect.TypeOf(callback) if callbackType.Kind() != reflect.Func { panic("not a function") @@ -291,85 +288,45 @@ func (s *Instance) RequireFeatures(callback interface{}, optional bool) error { deps: featureTypes, callback: callback, } - - s.resolveLock.Lock() - foundAll := true - for _, d := range r.deps { - f := getFeature(s.features, d) - if f == nil { - foundAll = false - break - } - } - if foundAll { - s.resolveLock.Unlock() - return r.callbackResolution(s.features) - } else { - if optional { - s.pendingOptionalResolutions = append(s.pendingOptionalResolutions, r) - } else { - s.pendingResolutions = append(s.pendingResolutions, r) - } - s.resolveLock.Unlock() - return nil + if finished, err := r.resolve(s.features); finished { + return err } + s.featureResolutions = append(s.featureResolutions, r) + return nil } // AddFeature registers a feature into current Instance. func (s *Instance) AddFeature(feature features.Feature) error { + s.features = append(s.features, feature) + if s.running { if err := feature.Start(); err != nil { - errors.LogInfoInner(s.ctx, err, "failed to start feature") + newError("failed to start feature").Base(err).WriteToLog() } return nil } - s.resolveLock.Lock() - s.features = append(s.features, feature) + if s.featureResolutions == nil { + return nil + } - var availableResolution []resolution - var pending []resolution - for _, r := range s.pendingResolutions { - foundAll := true - for _, d := range r.deps { - f := getFeature(s.features, d) - if f == nil { - foundAll = false - break - } + var pendingResolutions []resolution + for _, r := range s.featureResolutions { + finished, err := r.resolve(s.features) + if finished && err != nil { + return err } - if foundAll { - availableResolution = append(availableResolution, r) - } else { - pending = append(pending, r) + if !finished { + pendingResolutions = append(pendingResolutions, r) } } - s.pendingResolutions = pending - - var pendingOptional []resolution - for _, r := range s.pendingOptionalResolutions { - foundAll := true - for _, d := range r.deps { - f := getFeature(s.features, d) - if f == nil { - foundAll = false - break - } - } - if foundAll { - availableResolution = append(availableResolution, r) - } else { - pendingOptional = append(pendingOptional, r) - } + if len(pendingResolutions) == 0 { + s.featureResolutions = nil + } else if len(pendingResolutions) < len(s.featureResolutions) { + s.featureResolutions = pendingResolutions } - s.pendingOptionalResolutions = pendingOptional - s.resolveLock.Unlock() - var err error - for _, r := range availableResolution { - err = r.callbackResolution(s.features) // only return the last error for now - } - return err + return nil } // GetFeature returns a feature of the given type, or nil if such feature is not registered. @@ -382,8 +339,8 @@ func (s *Instance) GetFeature(featureType interface{}) features.Feature { // // xray:api:stable func (s *Instance) Start() error { - s.statusLock.Lock() - defer s.statusLock.Unlock() + s.access.Lock() + defer s.access.Unlock() s.running = true for _, f := range s.features { @@ -392,7 +349,7 @@ func (s *Instance) Start() error { } } - errors.LogWarning(s.ctx, "Xray ", Version(), " started") + newError("Xray ", Version(), " started").AtWarning().WriteToLog() return nil } diff --git a/core/xray_test.go b/core/xray_test.go index f4cb11ab..59de0f46 100644 --- a/core/xray_test.go +++ b/core/xray_test.go @@ -3,6 +3,7 @@ package core_test import ( "testing" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/dispatcher" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/common" @@ -18,7 +19,6 @@ import ( "github.com/xtls/xray-core/proxy/vmess" "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" - "google.golang.org/protobuf/proto" ) func TestXrayDependency(t *testing.T) { @@ -30,7 +30,7 @@ func TestXrayDependency(t *testing.T) { t.Error("expected dns client fulfilled, but actually nil") } wait <- true - }, false) + }) instance.AddFeature(localdns.New()) <-wait } @@ -54,9 +54,11 @@ func TestXrayClose(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(0), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(0), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, diff --git a/features/dns/client.go b/features/dns/client.go index 2a2bd148..d3a911da 100644 --- a/features/dns/client.go +++ b/features/dns/client.go @@ -21,7 +21,11 @@ type Client interface { features.Feature // LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses. - LookupIP(domain string, option IPOption) ([]net.IP, uint32, error) + LookupIP(domain string, option IPOption) ([]net.IP, error) +} + +type HostsLookup interface { + LookupHosts(domain string) *net.Address } // ClientType returns the type of Client interface. Can be used for implementing common.HasType. @@ -34,8 +38,6 @@ func ClientType() interface{} { // ErrEmptyResponse indicates that DNS query succeeded but no answer was returned. var ErrEmptyResponse = errors.New("empty response") -const DefaultTTL = 300 - type RCodeError uint16 func (e RCodeError) Error() string { diff --git a/features/dns/localdns/client.go b/features/dns/localdns/client.go index 48e740ee..92419dfa 100644 --- a/features/dns/localdns/client.go +++ b/features/dns/localdns/client.go @@ -20,44 +20,41 @@ func (*Client) Start() error { return nil } func (*Client) Close() error { return nil } // LookupIP implements Client. -func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, error) { +func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, error) { ips, err := net.LookupIP(host) if err != nil { - return nil, 0, err + return nil, err } parsedIPs := make([]net.IP, 0, len(ips)) ipv4 := make([]net.IP, 0, len(ips)) ipv6 := make([]net.IP, 0, len(ips)) for _, ip := range ips { parsed := net.IPAddress(ip) - if parsed == nil { - continue + if parsed != nil { + parsedIPs = append(parsedIPs, parsed.IP()) } - parsedIP := parsed.IP() - parsedIPs = append(parsedIPs, parsedIP) - - if len(parsedIP) == net.IPv4len { - ipv4 = append(ipv4, parsedIP) - } else { - ipv6 = append(ipv6, parsedIP) + if len(ip) == net.IPv4len { + ipv4 = append(ipv4, ip) + } + if len(ip) == net.IPv6len { + ipv6 = append(ipv6, ip) } } - switch { case option.IPv4Enable && option.IPv6Enable: if len(parsedIPs) > 0 { - return parsedIPs, dns.DefaultTTL, nil + return parsedIPs, nil } case option.IPv4Enable: if len(ipv4) > 0 { - return ipv4, dns.DefaultTTL, nil + return ipv4, nil } case option.IPv6Enable: if len(ipv6) > 0 { - return ipv6, dns.DefaultTTL, nil + return ipv6, nil } } - return nil, 0, dns.ErrEmptyResponse + return nil, dns.ErrEmptyResponse } // New create a new dns.Client that queries localhost for DNS. diff --git a/features/dns/localdns/errors.generated.go b/features/dns/localdns/errors.generated.go new file mode 100644 index 00000000..a501d5e6 --- /dev/null +++ b/features/dns/localdns/errors.generated.go @@ -0,0 +1,9 @@ +package localdns + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/features/errors.generated.go b/features/errors.generated.go new file mode 100644 index 00000000..ee8a5182 --- /dev/null +++ b/features/errors.generated.go @@ -0,0 +1,9 @@ +package features + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/features/extension/observatory.go b/features/extension/observatory.go index 8e871030..eb51a61b 100644 --- a/features/extension/observatory.go +++ b/features/extension/observatory.go @@ -3,8 +3,8 @@ package extension import ( "context" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/features" - "google.golang.org/protobuf/proto" ) type Observatory interface { diff --git a/features/feature.go b/features/feature.go index 026b8fa5..1288a86b 100644 --- a/features/feature.go +++ b/features/feature.go @@ -1,8 +1,8 @@ package features -import ( - "github.com/xtls/xray-core/common" -) +import "github.com/xtls/xray-core/common" + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen // Feature is the interface for Xray features. All features must implement this interface. // All existing features have an implementation in app directory. These features can be replaced by third-party ones. @@ -10,3 +10,8 @@ type Feature interface { common.HasType common.Runnable } + +// PrintDeprecatedFeatureWarning prints a warning for deprecated feature. +func PrintDeprecatedFeatureWarning(feature string) { + newError("You are using a deprecated feature: " + feature + ". Please update your config file with latest configuration format, or update your client software.").WriteToLog() +} diff --git a/features/inbound/inbound.go b/features/inbound/inbound.go index 1d3ba7b1..fd3a3db5 100644 --- a/features/inbound/inbound.go +++ b/features/inbound/inbound.go @@ -5,7 +5,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/features" ) @@ -16,10 +15,6 @@ type Handler interface { common.Runnable // The tag of this handler. Tag() string - // Returns the active receiver settings. - ReceiverSettings() *serial.TypedMessage - // Returns the active proxy settings. - ProxySettings() *serial.TypedMessage // Deprecated: Do not use in new code. GetRandomInboundProxy() (interface{}, net.Port, int) @@ -30,16 +25,13 @@ type Handler interface { // xray:api:stable type Manager interface { features.Feature - // GetHandler returns an InboundHandler for the given tag. + // GetHandlers returns an InboundHandler for the given tag. GetHandler(ctx context.Context, tag string) (Handler, error) // AddHandler adds the given handler into this Manager. AddHandler(ctx context.Context, handler Handler) error // RemoveHandler removes a handler from Manager. RemoveHandler(ctx context.Context, tag string) error - - // ListHandlers returns a list of inbound.Handler. - ListHandlers(ctx context.Context) []Handler } // ManagerType returns the type of Manager interface. Can be used for implementing common.HasType. diff --git a/features/outbound/outbound.go b/features/outbound/outbound.go index ecde6e1e..ed875439 100644 --- a/features/outbound/outbound.go +++ b/features/outbound/outbound.go @@ -4,7 +4,6 @@ import ( "context" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/transport" ) @@ -16,8 +15,6 @@ type Handler interface { common.Runnable Tag() string Dispatch(ctx context.Context, link *transport.Link) - SenderSettings() *serial.TypedMessage - ProxySettings() *serial.TypedMessage } type HandlerSelector interface { @@ -38,9 +35,6 @@ type Manager interface { // RemoveHandler removes a handler from outbound.Manager. RemoveHandler(ctx context.Context, tag string) error - - // ListHandlers returns a list of outbound.Handler. - ListHandlers(ctx context.Context) []Handler } // ManagerType returns the type of Manager interface. Can be used to implement common.HasType. diff --git a/features/policy/policy.go b/features/policy/policy.go index d6fd20d0..c3d48e6a 100644 --- a/features/policy/policy.go +++ b/features/policy/policy.go @@ -27,8 +27,6 @@ type Stats struct { UserUplink bool // Whether or not to enable stat counter for user downlink traffic. UserDownlink bool - // Whether or not to enable online map for user. - UserOnline bool } // Buffer contains settings for internal buffer. @@ -85,8 +83,12 @@ func ManagerType() interface{} { var defaultBufferSize int32 func init() { + const key = "xray.ray.buffer.size" const defaultValue = -17 - size := platform.NewEnvFlag(platform.BufferSize).GetValueAsInt(defaultValue) + size := platform.EnvFlag{ + Name: key, + AltName: platform.NormalizeEnvName(key), + }.GetValueAsInt(defaultValue) switch size { case 0: @@ -125,7 +127,6 @@ func SessionDefault() Session { Stats: Stats{ UserUplink: false, UserDownlink: false, - UserOnline: false, }, Buffer: defaultBufferPolicy(), } diff --git a/features/routing/balancer.go b/features/routing/balancer.go deleted file mode 100644 index b1a2339f..00000000 --- a/features/routing/balancer.go +++ /dev/null @@ -1,10 +0,0 @@ -package routing - -type BalancerOverrider interface { - SetOverrideTarget(tag, target string) error - GetOverrideTarget(tag string) (string, error) -} - -type BalancerPrincipleTarget interface { - GetPrincipleTarget(tag string) ([]string, error) -} diff --git a/features/routing/context.go b/features/routing/context.go index e52e2470..e7867c32 100644 --- a/features/routing/context.go +++ b/features/routing/context.go @@ -11,7 +11,7 @@ type Context interface { // GetInboundTag returns the tag of the inbound the connection was from. GetInboundTag() string - // GetSourceIPs returns the source IPs bound to the connection. + // GetSourcesIPs returns the source IPs bound to the connection. GetSourceIPs() []net.IP // GetSourcePort returns the source port of the connection. diff --git a/features/routing/dns/context.go b/features/routing/dns/context.go index a65895f6..7b7fe928 100644 --- a/features/routing/dns/context.go +++ b/features/routing/dns/context.go @@ -1,9 +1,8 @@ package dns -import ( - "context" +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen - "github.com/xtls/xray-core/common/errors" +import ( "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/routing" @@ -23,7 +22,7 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP { } if domain := ctx.GetTargetDomain(); len(domain) != 0 { - ips, _, err := ctx.dnsClient.LookupIP(domain, dns.IPOption{ + ips, err := ctx.dnsClient.LookupIP(domain, dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -32,7 +31,7 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP { ctx.resolvedIPs = ips return ips } - errors.LogInfoInner(context.Background(), err, "resolve ip for ", domain) + newError("resolve ip for ", domain).Base(err).WriteToLog() } if ips := ctx.Context.GetTargetIPs(); len(ips) != 0 { diff --git a/features/routing/dns/errors.generated.go b/features/routing/dns/errors.generated.go new file mode 100644 index 00000000..d7375a9b --- /dev/null +++ b/features/routing/dns/errors.generated.go @@ -0,0 +1,9 @@ +package dns + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/features/routing/router.go b/features/routing/router.go index 174d59fd..3d6f150a 100644 --- a/features/routing/router.go +++ b/features/routing/router.go @@ -2,7 +2,6 @@ package routing import ( "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/features" ) @@ -14,8 +13,6 @@ type Router interface { // PickRoute returns a route decision based on the given routing context. PickRoute(ctx Context) (Route, error) - AddRule(config *serial.TypedMessage, shouldAppend bool) error - RemoveRule(tag string) error } // Route is the routing result of Router feature. @@ -30,9 +27,6 @@ type Route interface { // GetOutboundTag returns the tag of the outbound the connection was dispatched to. GetOutboundTag() string - - // GetRuleTag returns the matching rule tag for debugging if exists - GetRuleTag() string } // RouterType return the type of Router interface. Can be used to implement common.HasType. @@ -55,16 +49,6 @@ func (DefaultRouter) PickRoute(ctx Context) (Route, error) { return nil, common.ErrNoClue } -// AddRule implements Router. -func (DefaultRouter) AddRule(config *serial.TypedMessage, shouldAppend bool) error { - return common.ErrNoClue -} - -// RemoveRule implements Router. -func (DefaultRouter) RemoveRule(tag string) error { - return common.ErrNoClue -} - // Start implements common.Runnable. func (DefaultRouter) Start() error { return nil diff --git a/features/routing/session/context.go b/features/routing/session/context.go index f87066a0..c900219d 100644 --- a/features/routing/session/context.go +++ b/features/routing/session/context.go @@ -124,11 +124,9 @@ func (ctx *Context) GetSkipDNSResolve() bool { // AsRoutingContext creates a context from context.context with session info. func AsRoutingContext(ctx context.Context) routing.Context { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] return &Context{ Inbound: session.InboundFromContext(ctx), - Outbound: ob, + Outbound: session.OutboundFromContext(ctx), Content: session.ContentFromContext(ctx), } } diff --git a/features/stats/errors.generated.go b/features/stats/errors.generated.go new file mode 100644 index 00000000..c64386f3 --- /dev/null +++ b/features/stats/errors.generated.go @@ -0,0 +1,9 @@ +package stats + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/features/stats/stats.go b/features/stats/stats.go index ab5b4067..bf83df78 100644 --- a/features/stats/stats.go +++ b/features/stats/stats.go @@ -1,11 +1,11 @@ package stats +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - "time" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/features" ) @@ -21,20 +21,6 @@ type Counter interface { Add(int64) int64 } -// OnlineMap is the interface for stats. -// -// xray:api:stable -type OnlineMap interface { - // Count is the current value of the OnlineMap. - Count() int - // AddIP adds a ip to the current OnlineMap. - AddIP(string) - // List is the current OnlineMap ip list. - List() []string - // IpTimeMap return client ips and their last access time. - IpTimeMap() map[string]time.Time -} - // Channel is the interface for stats channel. // // xray:api:stable @@ -85,13 +71,6 @@ type Manager interface { // GetCounter returns a counter by its identifier. GetCounter(string) Counter - // RegisterOnlineMap registers a new onlinemap to the manager. The identifier string must not be empty, and unique among other onlinemaps. - RegisterOnlineMap(string) (OnlineMap, error) - // UnregisterOnlineMap unregisters a onlinemap from the manager by its identifier. - UnregisterOnlineMap(string) error - // GetOnlineMap returns a onlinemap by its identifier. - GetOnlineMap(string) OnlineMap - // RegisterChannel registers a new channel to the manager. The identifier string must not be empty, and unique among other channels. RegisterChannel(string) (Channel, error) // UnregisterChannel unregisters a channel from the manager by its identifier. @@ -110,16 +89,6 @@ func GetOrRegisterCounter(m Manager, name string) (Counter, error) { return m.RegisterCounter(name) } -// GetOrRegisterOnlineMap tries to get the OnlineMap first. If not exist, it then tries to create a new onlinemap. -func GetOrRegisterOnlineMap(m Manager, name string) (OnlineMap, error) { - onlineMap := m.GetOnlineMap(name) - if onlineMap != nil { - return onlineMap, nil - } - - return m.RegisterOnlineMap(name) -} - // GetOrRegisterChannel tries to get the StatChannel first. If not exist, it then tries to create a new channel. func GetOrRegisterChannel(m Manager, name string) (Channel, error) { channel := m.GetChannel(name) @@ -147,7 +116,7 @@ func (NoopManager) Type() interface{} { // RegisterCounter implements Manager. func (NoopManager) RegisterCounter(string) (Counter, error) { - return nil, errors.New("not implemented") + return nil, newError("not implemented") } // UnregisterCounter implements Manager. @@ -160,24 +129,9 @@ func (NoopManager) GetCounter(string) Counter { return nil } -// RegisterOnlineMap implements Manager. -func (NoopManager) RegisterOnlineMap(string) (OnlineMap, error) { - return nil, errors.New("not implemented") -} - -// UnregisterOnlineMap implements Manager. -func (NoopManager) UnregisterOnlineMap(string) error { - return nil -} - -// GetOnlineMap implements Manager. -func (NoopManager) GetOnlineMap(string) OnlineMap { - return nil -} - // RegisterChannel implements Manager. func (NoopManager) RegisterChannel(string) (Channel, error) { - return nil, errors.New("not implemented") + return nil, newError("not implemented") } // UnregisterChannel implements Manager. diff --git a/go.mod b/go.mod index efe9ac45..58f1cd65 100644 --- a/go.mod +++ b/go.mod @@ -1,61 +1,59 @@ module github.com/xtls/xray-core -go 1.24 +go 1.20 require ( - github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 - github.com/cloudflare/circl v1.6.1 github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 - github.com/golang/mock v1.7.0-rc.1 - github.com/google/go-cmp v0.7.0 - github.com/gorilla/websocket v1.5.3 - github.com/miekg/dns v1.1.67 + github.com/golang/mock v1.6.0 + github.com/golang/protobuf v1.5.3 + github.com/google/go-cmp v0.5.9 + github.com/gorilla/websocket v1.5.0 + github.com/miekg/dns v1.1.51 github.com/pelletier/go-toml v1.9.5 - github.com/pires/go-proxyproto v0.8.1 - github.com/quic-go/quic-go v0.52.0 - github.com/refraction-networking/utls v1.7.3 - github.com/sagernet/sing v0.5.1 - github.com/sagernet/sing-shadowsocks v0.2.7 - github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 - github.com/stretchr/testify v1.10.0 + github.com/pires/go-proxyproto v0.6.2 + github.com/quic-go/quic-go v0.33.0 + github.com/refraction-networking/utls v1.2.3-0.20230308205431-4f1df6c200db + github.com/sagernet/sing v0.1.7 + github.com/sagernet/sing-shadowsocks v0.1.1 + github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c + github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb + github.com/stretchr/testify v1.8.2 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e - github.com/vishvananda/netlink v1.3.1 - github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb - go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.40.0 - golang.org/x/net v0.41.0 - golang.org/x/sync v0.16.0 - golang.org/x/sys v0.34.0 - golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 - google.golang.org/grpc v1.73.0 - google.golang.org/protobuf v1.36.6 - gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 + github.com/xtls/reality v0.0.0-20230309125256-0d0713b108c8 + go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 + golang.org/x/crypto v0.7.0 + golang.org/x/net v0.8.0 + golang.org/x/sync v0.1.0 + golang.org/x/sys v0.6.0 + google.golang.org/grpc v1.53.0 + google.golang.org/protobuf v1.29.0 + gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h12.io/socks v1.0.3 - lukechampine.com/blake3 v1.4.1 ) require ( - github.com/andybalholm/brotli v1.1.0 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect - github.com/juju/ratelimit v1.0.2 // indirect - github.com/klauspost/compress v1.17.8 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/onsi/ginkgo/v2 v2.19.0 // indirect + github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/onsi/ginkgo/v2 v2.9.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/qtls-go1-19 v0.2.1 // indirect + github.com/quic-go/qtls-go1-20 v0.1.1 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect - github.com/vishvananda/netns v0.0.5 // indirect - go.uber.org/mock v0.5.0 // indirect - golang.org/x/mod v0.25.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.34.0 // indirect - golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect + go.uber.org/atomic v1.10.0 // indirect + golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect + golang.org/x/mod v0.9.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.7.0 // indirect + google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index e2958d66..ab32e439 100644 --- a/go.sum +++ b/go.sum @@ -1,156 +1,344 @@ -github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I= -github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= -github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g= -github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso= +github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= -github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= -github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0= -github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo= +github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= +github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= +github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= -github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= -github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= +github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= +github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA= -github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= -github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo= -github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= +github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= +github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= +github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= +github.com/refraction-networking/utls v1.2.3-0.20230308205431-4f1df6c200db h1:ULRv/GPW5KYDafE0FACN2no+HTCyQLUtfyOIeyp3GNc= +github.com/refraction-networking/utls v1.2.3-0.20230308205431-4f1df6c200db/go.mod h1:kHXvVB66a4BzVRYC4Em7e1HAfp7uwOCCw0+2CZ3sMY8= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= -github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y= -github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= -github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= -github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4= -github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sagernet/sing v0.1.7 h1:g4vjr3q8SUlBZSx97Emz5OBfSMBxxW5Q8C2PfdoSo08= +github.com/sagernet/sing v0.1.7/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing-shadowsocks v0.1.1 h1:uFK2rlVeD/b1xhDwSMbUI2goWc6fOKxp+ZeKHZq6C9Q= +github.com/sagernet/sing-shadowsocks v0.1.1/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU= +github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= +github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= +github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= +github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= -github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= -github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= -github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= -github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb h1:X6ziJCMsFF8Ac/0F3W7+UbFdHZTu+r5nZ/smksHVxNQ= -github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb/go.mod h1:yD47RN65bDLZgyHWMfFDiqlzrq4usDMt/Xzsk6tMbhw= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= -go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/xtls/reality v0.0.0-20230309125256-0d0713b108c8 h1:LLtLxEe3S0Ko+ckqt4t29RLskpNdOZfgjZCC2/Byr50= +github.com/xtls/reality v0.0.0-20230309125256-0d0713b108c8/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 h1:Ss6D3hLXTM0KobyBYEAygXzFfGcjnmfEJOBgSbemCtg= +go.starlark.net v0.0.0-20230302034142-4b1e35fe2254/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= -golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= -golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0= +google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -158,9 +346,16 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 h1:sfK5nHuG7lRFZ2FdTT3RimOqWBg8IrVm+/Vko1FVOsk= -gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4= +gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo= h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= -lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= -lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= +lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/infra/conf/api.go b/infra/conf/api.go index dca34910..879fc328 100644 --- a/infra/conf/api.go +++ b/infra/conf/api.go @@ -7,21 +7,18 @@ import ( loggerservice "github.com/xtls/xray-core/app/log/command" observatoryservice "github.com/xtls/xray-core/app/observatory/command" handlerservice "github.com/xtls/xray-core/app/proxyman/command" - routerservice "github.com/xtls/xray-core/app/router/command" statsservice "github.com/xtls/xray-core/app/stats/command" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/serial" ) type APIConfig struct { Tag string `json:"tag"` - Listen string `json:"listen"` Services []string `json:"services"` } func (c *APIConfig) Build() (*commander.Config, error) { if c.Tag == "" { - return nil, errors.New("API tag can't be empty.") + return nil, newError("API tag can't be empty.") } services := make([]*serial.TypedMessage, 0, 16) @@ -37,14 +34,11 @@ func (c *APIConfig) Build() (*commander.Config, error) { services = append(services, serial.ToTypedMessage(&statsservice.Config{})) case "observatoryservice": services = append(services, serial.ToTypedMessage(&observatoryservice.Config{})) - case "routingservice": - services = append(services, serial.ToTypedMessage(&routerservice.Config{})) } } return &commander.Config{ Tag: c.Tag, - Listen: c.Listen, Service: services, }, nil } diff --git a/infra/conf/blackhole.go b/infra/conf/blackhole.go index 7b78742f..c1551de1 100644 --- a/infra/conf/blackhole.go +++ b/infra/conf/blackhole.go @@ -3,10 +3,9 @@ package conf import ( "encoding/json" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/proxy/blackhole" - "google.golang.org/protobuf/proto" ) type NoneResponse struct{} @@ -30,7 +29,7 @@ func (v *BlackholeConfig) Build() (proto.Message, error) { if v.Response != nil { response, _, err := configLoader.Load(v.Response) if err != nil { - return nil, errors.New("Config: Failed to parse Blackhole response config.").Base(err) + return nil, newError("Config: Failed to parse Blackhole response config.").Base(err) } responseSettings, err := response.(Buildable).Build() if err != nil { diff --git a/infra/conf/buildable.go b/infra/conf/buildable.go index 967e9740..1d01cd66 100644 --- a/infra/conf/buildable.go +++ b/infra/conf/buildable.go @@ -1,6 +1,6 @@ package conf -import "google.golang.org/protobuf/proto" +import "github.com/golang/protobuf/proto" type Buildable interface { Build() (proto.Message, error) diff --git a/infra/conf/cfgcommon/duration/duration.go b/infra/conf/cfgcommon/duration/duration.go index f1bbd4d7..aed8e613 100644 --- a/infra/conf/cfgcommon/duration/duration.go +++ b/infra/conf/cfgcommon/duration/duration.go @@ -8,13 +8,11 @@ import ( type Duration int64 -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON func (d *Duration) MarshalJSON() ([]byte, error) { dr := time.Duration(*d) return json.Marshal(dr.String()) } -// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (d *Duration) UnmarshalJSON(b []byte) error { var v interface{} if err := json.Unmarshal(b, &v); err != nil { diff --git a/infra/conf/common.go b/infra/conf/common.go index ab3cfba7..486e7824 100644 --- a/infra/conf/common.go +++ b/infra/conf/common.go @@ -2,13 +2,10 @@ package conf import ( "encoding/json" - "fmt" - "strconv" + "os" "strings" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/protocol" ) @@ -23,7 +20,6 @@ func (v StringList) Len() int { return len(v) } -// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (v *StringList) UnmarshalJSON(data []byte) error { var strarray []string if err := json.Unmarshal(data, &strarray); err == nil { @@ -37,26 +33,17 @@ func (v *StringList) UnmarshalJSON(data []byte) error { *v = *NewStringList(strlist) return nil } - return errors.New("unknown format of a string list: " + string(data)) + return newError("unknown format of a string list: " + string(data)) } type Address struct { net.Address } -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON -func (v *Address) MarshalJSON() ([]byte, error) { - return json.Marshal(v.Address.String()) -} - -// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (v *Address) UnmarshalJSON(data []byte) error { var rawStr string if err := json.Unmarshal(data, &rawStr); err != nil { - return errors.New("invalid address: ", string(data)).Base(err) - } - if strings.HasPrefix(rawStr, "env:") { - rawStr = platform.NewEnvFlag(rawStr[4:]).GetValue(func() string { return "" }) + return newError("invalid address: ", string(data)).Base(err) } v.Address = net.ParseAddress(rawStr) @@ -84,7 +71,6 @@ func (v Network) Build() net.Network { type NetworkList []Network -// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (v *NetworkList) UnmarshalJSON(data []byte) error { var strarray []Network if err := json.Unmarshal(data, &strarray); err == nil { @@ -103,7 +89,7 @@ func (v *NetworkList) UnmarshalJSON(data []byte) error { *v = nl return nil } - return errors.New("unknown format of a string list: " + string(data)) + return newError("unknown format of a string list: " + string(data)) } func (v *NetworkList) Build() []net.Network { @@ -129,12 +115,13 @@ func parseIntPort(data []byte) (net.Port, error) { func parseStringPort(s string) (net.Port, net.Port, error) { if strings.HasPrefix(s, "env:") { - s = platform.NewEnvFlag(s[4:]).GetValue(func() string { return "" }) + s = s[4:] + s = os.Getenv(s) } pair := strings.SplitN(s, "-", 2) if len(pair) == 0 { - return net.Port(0), net.Port(0), errors.New("invalid port range: ", s) + return net.Port(0), net.Port(0), newError("invalid port range: ", s) } if len(pair) == 1 { port, err := net.PortFromString(pair[0]) @@ -173,19 +160,6 @@ func (v *PortRange) Build() *net.PortRange { } } -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON -func (v *PortRange) MarshalJSON() ([]byte, error) { - return json.Marshal(v.String()) -} - -func (port *PortRange) String() string { - if port.From == port.To { - return strconv.Itoa(int(port.From)) - } else { - return fmt.Sprintf("%d-%d", port.From, port.To) - } -} - // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (v *PortRange) UnmarshalJSON(data []byte) error { port, err := parseIntPort(data) @@ -200,12 +174,12 @@ func (v *PortRange) UnmarshalJSON(data []byte) error { v.From = uint32(from) v.To = uint32(to) if v.From > v.To { - return errors.New("invalid port range ", v.From, " -> ", v.To) + return newError("invalid port range ", v.From, " -> ", v.To) } return nil } - return errors.New("invalid port range: ", string(data)) + return newError("invalid port range: ", string(data)) } type PortList struct { @@ -220,32 +194,13 @@ func (list *PortList) Build() *net.PortList { return portList } -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON -func (v *PortList) MarshalJSON() ([]byte, error) { - portStr := v.String() - port, err := strconv.Atoi(portStr) - if err == nil { - return json.Marshal(port) - } else { - return json.Marshal(portStr) - } -} - -func (v PortList) String() string { - ports := []string{} - for _, port := range v.Range { - ports = append(ports, port.String()) - } - return strings.Join(ports, ",") -} - // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (list *PortList) UnmarshalJSON(data []byte) error { var listStr string var number uint32 if err := json.Unmarshal(data, &listStr); err != nil { if err2 := json.Unmarshal(data, &number); err2 != nil { - return errors.New("invalid port: ", string(data)).Base(err2) + return newError("invalid port: ", string(data)).Base(err2) } } rangelist := strings.Split(listStr, ",") @@ -255,13 +210,13 @@ func (list *PortList) UnmarshalJSON(data []byte) error { if strings.Contains(trimmed, "-") || strings.Contains(trimmed, "env:") { from, to, err := parseStringPort(trimmed) if err != nil { - return errors.New("invalid port range: ", trimmed).Base(err) + return newError("invalid port range: ", trimmed).Base(err) } list.Range = append(list.Range, PortRange{From: uint32(from), To: uint32(to)}) } else { port, err := parseIntPort([]byte(trimmed)) if err != nil { - return errors.New("invalid port: ", trimmed).Base(err) + return newError("invalid port: ", trimmed).Base(err) } list.Range = append(list.Range, PortRange{From: uint32(port), To: uint32(port)}) } @@ -284,93 +239,3 @@ func (v *User) Build() *protocol.User { Level: uint32(v.LevelByte), } } - -// Int32Range deserializes from "1-2" or 1, so can deserialize from both int and number. -// Negative integers can be passed as sentinel values, but do not parse as ranges. -// Value will be exchanged if From > To, use .Left and .Right to get original value if need. -type Int32Range struct { - Left int32 - Right int32 - From int32 - To int32 -} - -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON -func (v *Int32Range) MarshalJSON() ([]byte, error) { - return json.Marshal(v.String()) -} - -func (v Int32Range) String() string { - if v.Left == v.Right { - return strconv.Itoa(int(v.Left)) - } else { - return fmt.Sprintf("%d-%d", v.Left, v.Right) - } -} - -// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON -func (v *Int32Range) UnmarshalJSON(data []byte) error { - defer v.ensureOrder() - var str string - var rawint int32 - if err := json.Unmarshal(data, &str); err == nil { - left, right, err := ParseRangeString(str) - if err == nil { - v.Left, v.Right = int32(left), int32(right) - return nil - } - } else if err := json.Unmarshal(data, &rawint); err == nil { - v.Left = rawint - v.Right = rawint - return nil - } - - return errors.New("Invalid integer range, expected either string of form \"1-2\" or plain integer.") -} - -// ensureOrder() gives value to .From & .To and make sure .From < .To -func (r *Int32Range) ensureOrder() { - r.From, r.To = r.Left, r.Right - if r.From > r.To { - r.From, r.To = r.To, r.From - } -} - -// "-114-514" → ["-114","514"] -// "-1919--810" → ["-1919","-810"] -func splitFromSecondDash(s string) []string { - parts := strings.SplitN(s, "-", 3) - if len(parts) < 3 { - return []string{s} - } - return []string{parts[0] + "-" + parts[1], parts[2]} -} - -// Parse rang in string. Support negative number. -// eg: "114-514" "-114-514" "-1919--810" "114514" ""(return 0) -func ParseRangeString(str string) (int, int, error) { - // for number in string format like "114" or "-1" - if value, err := strconv.Atoi(str); err == nil { - return value, value, nil - } - // for empty "", we treat it as 0 - if str == "" { - return 0, 0, nil - } - // for range value, like "114-514" - var pair []string - // Process sth like "-114-514" "-1919--810" - if strings.HasPrefix(str, "-") { - pair = splitFromSecondDash(str) - } else { - pair = strings.SplitN(str, "-", 2) - } - if len(pair) == 2 { - left, err := strconv.Atoi(pair[0]) - right, err2 := strconv.Atoi(pair[1]) - if err == nil && err2 == nil { - return left, right, nil - } - } - return 0, 0, errors.New("invalid range string: ", str) -} diff --git a/infra/conf/conf.go b/infra/conf/conf.go index 8f52a955..9bd3f667 100644 --- a/infra/conf/conf.go +++ b/infra/conf/conf.go @@ -1 +1,3 @@ package conf + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/infra/conf/dns.go b/infra/conf/dns.go index 8a60bc00..3265b819 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -1,37 +1,24 @@ package conf import ( - "bufio" "encoding/json" - "os" - "path/filepath" - "runtime" "sort" "strings" "github.com/xtls/xray-core/app/dns" "github.com/xtls/xray-core/app/router" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" ) type NameServerConfig struct { - Address *Address `json:"address"` - ClientIP *Address `json:"clientIp"` - Port uint16 `json:"port"` - SkipFallback bool `json:"skipFallback"` - Domains []string `json:"domains"` - ExpectedIPs StringList `json:"expectedIPs"` - ExpectIPs StringList `json:"expectIPs"` - QueryStrategy string `json:"queryStrategy"` - Tag string `json:"tag"` - TimeoutMs uint64 `json:"timeoutMs"` - DisableCache bool `json:"disableCache"` - FinalQuery bool `json:"finalQuery"` - UnexpectedIPs StringList `json:"unexpectedIPs"` + Address *Address + ClientIP *Address + Port uint16 + SkipFallback bool + Domains []string + ExpectIPs StringList } -// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (c *NameServerConfig) UnmarshalJSON(data []byte) error { var address Address if err := json.Unmarshal(data, &address); err == nil { @@ -40,19 +27,12 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error { } var advanced struct { - Address *Address `json:"address"` - ClientIP *Address `json:"clientIp"` - Port uint16 `json:"port"` - SkipFallback bool `json:"skipFallback"` - Domains []string `json:"domains"` - ExpectedIPs StringList `json:"expectedIPs"` - ExpectIPs StringList `json:"expectIPs"` - QueryStrategy string `json:"queryStrategy"` - Tag string `json:"tag"` - TimeoutMs uint64 `json:"timeoutMs"` - DisableCache bool `json:"disableCache"` - FinalQuery bool `json:"finalQuery"` - UnexpectedIPs StringList `json:"unexpectedIPs"` + Address *Address `json:"address"` + ClientIP *Address `json:"clientIp"` + Port uint16 `json:"port"` + SkipFallback bool `json:"skipFallback"` + Domains []string `json:"domains"` + ExpectIPs StringList `json:"expectIps"` } if err := json.Unmarshal(data, &advanced); err == nil { c.Address = advanced.Address @@ -60,18 +40,11 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error { c.Port = advanced.Port c.SkipFallback = advanced.SkipFallback c.Domains = advanced.Domains - c.ExpectedIPs = advanced.ExpectedIPs c.ExpectIPs = advanced.ExpectIPs - c.QueryStrategy = advanced.QueryStrategy - c.Tag = advanced.Tag - c.TimeoutMs = advanced.TimeoutMs - c.DisableCache = advanced.DisableCache - c.FinalQuery = advanced.FinalQuery - c.UnexpectedIPs = advanced.UnexpectedIPs return nil } - return errors.New("failed to parse name server: ", string(data)) + return newError("failed to parse name server: ", string(data)) } func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType { @@ -91,7 +64,7 @@ func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType { func (c *NameServerConfig) Build() (*dns.NameServer, error) { if c.Address == nil { - return nil, errors.New("NameServer address is not specified.") + return nil, newError("NameServer address is not specified.") } var domains []*dns.NameServer_PriorityDomain @@ -100,7 +73,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) { for _, rule := range c.Domains { parsedDomain, err := parseDomainRule(rule) if err != nil { - return nil, errors.New("invalid domain rule: ", rule).Base(err) + return nil, newError("invalid domain rule: ", rule).Base(err) } for _, pd := range parsedDomain { @@ -115,44 +88,15 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) { }) } - if len(c.ExpectedIPs) == 0 { - c.ExpectedIPs = c.ExpectIPs - } - - actPrior := false - var newExpectedIPs StringList - for _, s := range c.ExpectedIPs { - if s == "*" { - actPrior = true - } else { - newExpectedIPs = append(newExpectedIPs, s) - } - } - - actUnprior := false - var newUnexpectedIPs StringList - for _, s := range c.UnexpectedIPs { - if s == "*" { - actUnprior = true - } else { - newUnexpectedIPs = append(newUnexpectedIPs, s) - } - } - - expectedGeoipList, err := ToCidrList(newExpectedIPs) + geoipList, err := ToCidrList(c.ExpectIPs) if err != nil { - return nil, errors.New("invalid expected IP rule: ", c.ExpectedIPs).Base(err) - } - - unexpectedGeoipList, err := ToCidrList(newUnexpectedIPs) - if err != nil { - return nil, errors.New("invalid unexpected IP rule: ", c.UnexpectedIPs).Base(err) + return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err) } var myClientIP []byte if c.ClientIP != nil { if !c.ClientIP.Family().IsIP() { - return nil, errors.New("not an IP address:", c.ClientIP.String()) + return nil, newError("not an IP address:", c.ClientIP.String()) } myClientIP = []byte(c.ClientIP.IP()) } @@ -166,16 +110,8 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) { ClientIp: myClientIP, SkipFallback: c.SkipFallback, PrioritizedDomain: domains, - ExpectedGeoip: expectedGeoipList, + Geoip: geoipList, OriginalRules: originalRules, - QueryStrategy: resolveQueryStrategy(c.QueryStrategy), - ActPrior: actPrior, - Tag: c.Tag, - TimeoutMs: c.TimeoutMs, - DisableCache: c.DisableCache, - FinalQuery: c.FinalQuery, - UnexpectedGeoip: unexpectedGeoipList, - ActUnprior: actUnprior, }, nil } @@ -196,7 +132,6 @@ type DNSConfig struct { DisableCache bool `json:"disableCache"` DisableFallback bool `json:"disableFallback"` DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"` - UseSystemHosts bool `json:"useSystemHosts"` } type HostAddress struct { @@ -204,18 +139,6 @@ type HostAddress struct { addrs []*Address } -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON -func (h *HostAddress) MarshalJSON() ([]byte, error) { - if (h.addr != nil) != (h.addrs != nil) { - if h.addr != nil { - return json.Marshal(h.addr) - } else if h.addrs != nil { - return json.Marshal(h.addrs) - } - } - return nil, errors.New("unexpected config state") -} - // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (h *HostAddress) UnmarshalJSON(data []byte) error { addr := new(Address) @@ -226,7 +149,7 @@ func (h *HostAddress) UnmarshalJSON(data []byte) error { case json.Unmarshal(data, &addrs) == nil: h.addrs = addrs default: - return errors.New("invalid address") + return newError("invalid address") } return nil } @@ -261,11 +184,6 @@ func getHostMapping(ha *HostAddress) *dns.Config_HostMapping { } } -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON -func (m *HostsWrapper) MarshalJSON() ([]byte, error) { - return json.Marshal(m.Hosts) -} - // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (m *HostsWrapper) UnmarshalJSON(data []byte) error { hosts := make(map[string]*HostAddress) @@ -274,7 +192,7 @@ func (m *HostsWrapper) UnmarshalJSON(data []byte) error { m.Hosts = hosts return nil } - return errors.New("invalid DNS hosts").Base(err) + return newError("invalid DNS hosts").Base(err) } // Build implements Buildable @@ -292,7 +210,7 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) { case strings.HasPrefix(domain, "domain:"): domainName := domain[7:] if len(domainName) == 0 { - return nil, errors.New("empty domain type of rule: ", domain) + return nil, newError("empty domain type of rule: ", domain) } mapping := getHostMapping(m.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Subdomain @@ -302,11 +220,11 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) { case strings.HasPrefix(domain, "geosite:"): listName := domain[8:] if len(listName) == 0 { - return nil, errors.New("empty geosite rule: ", domain) + return nil, newError("empty geosite rule: ", domain) } geositeList, err := loadGeositeWithAttr("geosite.dat", listName) if err != nil { - return nil, errors.New("failed to load geosite: ", listName).Base(err) + return nil, newError("failed to load geosite: ", listName).Base(err) } for _, d := range geositeList { mapping := getHostMapping(m.Hosts[domain]) @@ -318,7 +236,7 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) { case strings.HasPrefix(domain, "regexp:"): regexpVal := domain[7:] if len(regexpVal) == 0 { - return nil, errors.New("empty regexp type of rule: ", domain) + return nil, newError("empty regexp type of rule: ", domain) } mapping := getHostMapping(m.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Regex @@ -328,7 +246,7 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) { case strings.HasPrefix(domain, "keyword:"): keywordVal := domain[8:] if len(keywordVal) == 0 { - return nil, errors.New("empty keyword type of rule: ", domain) + return nil, newError("empty keyword type of rule: ", domain) } mapping := getHostMapping(m.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Keyword @@ -338,7 +256,7 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) { case strings.HasPrefix(domain, "full:"): fullVal := domain[5:] if len(fullVal) == 0 { - return nil, errors.New("empty full domain type of rule: ", domain) + return nil, newError("empty full domain type of rule: ", domain) } mapping := getHostMapping(m.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Full @@ -354,20 +272,20 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) { case !strings.Contains(substr, "."): mapping.Domain = "^[^.]*" + substr + "[^.]*$" default: - return nil, errors.New("substr in dotless rule should not contain a dot: ", substr) + return nil, newError("substr in dotless rule should not contain a dot: ", substr) } mappings = append(mappings, mapping) case strings.HasPrefix(domain, "ext:"): kv := strings.Split(domain[4:], ":") if len(kv) != 2 { - return nil, errors.New("invalid external resource: ", domain) + return nil, newError("invalid external resource: ", domain) } filename := kv[0] list := kv[1] geositeList, err := loadGeositeWithAttr(filename, list) if err != nil { - return nil, errors.New("failed to load domain list: ", list, " from ", filename).Base(err) + return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err) } for _, d := range geositeList { mapping := getHostMapping(m.Hosts[domain]) @@ -393,20 +311,29 @@ func (c *DNSConfig) Build() (*dns.Config, error) { DisableCache: c.DisableCache, DisableFallback: c.DisableFallback, DisableFallbackIfMatch: c.DisableFallbackIfMatch, - QueryStrategy: resolveQueryStrategy(c.QueryStrategy), } if c.ClientIP != nil { if !c.ClientIP.Family().IsIP() { - return nil, errors.New("not an IP address:", c.ClientIP.String()) + return nil, newError("not an IP address:", c.ClientIP.String()) } config.ClientIp = []byte(c.ClientIP.IP()) } + config.QueryStrategy = dns.QueryStrategy_USE_IP + switch strings.ToLower(c.QueryStrategy) { + case "useip", "use_ip", "use-ip": + config.QueryStrategy = dns.QueryStrategy_USE_IP + case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": + config.QueryStrategy = dns.QueryStrategy_USE_IP4 + case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": + config.QueryStrategy = dns.QueryStrategy_USE_IP6 + } + for _, server := range c.Servers { ns, err := server.Build() if err != nil { - return nil, errors.New("failed to build nameserver").Base(err) + return nil, newError("failed to build nameserver").Base(err) } config.NameServer = append(config.NameServer, ns) } @@ -414,82 +341,10 @@ func (c *DNSConfig) Build() (*dns.Config, error) { if c.Hosts != nil { staticHosts, err := c.Hosts.Build() if err != nil { - return nil, errors.New("failed to build hosts").Base(err) + return nil, newError("failed to build hosts").Base(err) } config.StaticHosts = append(config.StaticHosts, staticHosts...) } - if c.UseSystemHosts { - systemHosts, err := readSystemHosts() - if err != nil { - return nil, errors.New("failed to read system hosts").Base(err) - } - for domain, ips := range systemHosts { - config.StaticHosts = append(config.StaticHosts, &dns.Config_HostMapping{Ip: ips, Domain: domain, Type: dns.DomainMatchingType_Full}) - } - } return config, nil } - -func resolveQueryStrategy(queryStrategy string) dns.QueryStrategy { - switch strings.ToLower(queryStrategy) { - case "useip", "use_ip", "use-ip": - return dns.QueryStrategy_USE_IP - case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": - return dns.QueryStrategy_USE_IP4 - case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": - return dns.QueryStrategy_USE_IP6 - case "usesys", "usesystem", "use_sys", "use_system", "use-sys", "use-system": - return dns.QueryStrategy_USE_SYS - default: - return dns.QueryStrategy_USE_IP - } -} - -func readSystemHosts() (map[string][][]byte, error) { - var hostsPath string - switch runtime.GOOS { - case "windows": - hostsPath = filepath.Join(os.Getenv("SystemRoot"), "System32", "drivers", "etc", "hosts") - default: - hostsPath = "/etc/hosts" - } - - file, err := os.Open(hostsPath) - if err != nil { - return nil, err - } - defer file.Close() - - hostsMap := make(map[string][][]byte) - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if i := strings.IndexByte(line, '#'); i >= 0 { - // Discard comments. - line = line[0:i] - } - f := strings.Fields(line) - if len(f) < 2 { - continue - } - addr := net.ParseAddress(f[0]) - if addr.Family().IsDomain() { - continue - } - ip := addr.IP() - for i := 1; i < len(f); i++ { - domain := strings.TrimSuffix(f[i], ".") - domain = strings.ToLower(domain) - if v, ok := hostsMap[domain]; ok { - hostsMap[domain] = append(v, ip) - } else { - hostsMap[domain] = [][]byte{ip} - } - } - } - if err := scanner.Err(); err != nil { - return nil, err - } - return hostsMap, nil -} diff --git a/infra/conf/dns_proxy.go b/infra/conf/dns_proxy.go index e731ee7c..90a5d65e 100644 --- a/infra/conf/dns_proxy.go +++ b/infra/conf/dns_proxy.go @@ -1,19 +1,16 @@ package conf import ( - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/proxy/dns" - "google.golang.org/protobuf/proto" ) type DNSOutboundConfig struct { - Network Network `json:"network"` - Address *Address `json:"address"` - Port uint16 `json:"port"` - UserLevel uint32 `json:"userLevel"` - NonIPQuery string `json:"nonIPQuery"` - BlockTypes []int32 `json:"blockTypes"` + Network Network `json:"network"` + Address *Address `json:"address"` + Port uint16 `json:"port"` + UserLevel uint32 `json:"userLevel"` } func (c *DNSOutboundConfig) Build() (proto.Message, error) { @@ -27,14 +24,5 @@ func (c *DNSOutboundConfig) Build() (proto.Message, error) { if c.Address != nil { config.Server.Address = c.Address.Build() } - switch c.NonIPQuery { - case "": - c.NonIPQuery = "drop" - case "drop", "skip", "reject": - default: - return nil, errors.New(`unknown "nonIPQuery": `, c.NonIPQuery) - } - config.Non_IPQuery = c.NonIPQuery - config.BlockTypes = c.BlockTypes return config, nil } diff --git a/infra/conf/dns_proxy_test.go b/infra/conf/dns_proxy_test.go index 5c5dfecd..805ac323 100644 --- a/infra/conf/dns_proxy_test.go +++ b/infra/conf/dns_proxy_test.go @@ -27,7 +27,6 @@ func TestDnsProxyConfig(t *testing.T) { Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})), Port: 53, }, - Non_IPQuery: "drop", }, }, }) diff --git a/infra/conf/dns_test.go b/infra/conf/dns_test.go index 5d423884..c97c5bea 100644 --- a/infra/conf/dns_test.go +++ b/infra/conf/dns_test.go @@ -2,15 +2,57 @@ package conf_test import ( "encoding/json" + "os" + "path/filepath" "testing" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/dns" + "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" . "github.com/xtls/xray-core/infra/conf" - "google.golang.org/protobuf/proto" ) +func init() { + wd, err := os.Getwd() + common.Must(err) + + if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) { + common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "resources", "geoip.dat"))) + } + + geositeFilePath := filepath.Join(wd, "geosite.dat") + os.Setenv("xray.location.asset", wd) + geositeFile, err := os.OpenFile(geositeFilePath, os.O_CREATE|os.O_WRONLY, 0o600) + common.Must(err) + defer geositeFile.Close() + + list := &router.GeoSiteList{ + Entry: []*router.GeoSite{ + { + CountryCode: "TEST", + Domain: []*router.Domain{ + {Type: router.Domain_Full, Value: "example.com"}, + }, + }, + }, + } + + listBytes, err := proto.Marshal(list) + common.Must(err) + common.Must2(geositeFile.Write(listBytes)) +} + func TestDNSConfigParsing(t *testing.T) { + geositePath := platform.GetAssetLocation("geosite.dat") + defer func() { + os.Remove(geositePath) + os.Unsetenv("xray.location.asset") + }() + parserCreator := func() func(string) (proto.Message, error) { return func(s string) (proto.Message, error) { config := new(DNSConfig) diff --git a/infra/conf/dokodemo.go b/infra/conf/dokodemo.go index 2086cb5a..03a21d71 100644 --- a/infra/conf/dokodemo.go +++ b/infra/conf/dokodemo.go @@ -1,16 +1,17 @@ package conf import ( + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/proxy/dokodemo" - "google.golang.org/protobuf/proto" ) type DokodemoConfig struct { - Host *Address `json:"address"` - PortValue uint16 `json:"port"` - NetworkList *NetworkList `json:"network"` - Redirect bool `json:"followRedirect"` - UserLevel uint32 `json:"userLevel"` + Host *Address `json:"address"` + PortValue uint16 `json:"port"` + NetworkList *NetworkList `json:"network"` + TimeoutValue uint32 `json:"timeout"` + Redirect bool `json:"followRedirect"` + UserLevel uint32 `json:"userLevel"` } func (v *DokodemoConfig) Build() (proto.Message, error) { @@ -20,6 +21,7 @@ func (v *DokodemoConfig) Build() (proto.Message, error) { } config.Port = uint32(v.PortValue) config.Networks = v.NetworkList.Build() + config.Timeout = v.TimeoutValue config.FollowRedirect = v.Redirect config.UserLevel = v.UserLevel return config, nil diff --git a/infra/conf/dokodemo_test.go b/infra/conf/dokodemo_test.go index 264cc8d7..6432eb42 100644 --- a/infra/conf/dokodemo_test.go +++ b/infra/conf/dokodemo_test.go @@ -19,6 +19,7 @@ func TestDokodemoConfig(t *testing.T) { "address": "8.8.8.8", "port": 53, "network": "tcp", + "timeout": 10, "followRedirect": true, "userLevel": 1 }`, @@ -31,6 +32,7 @@ func TestDokodemoConfig(t *testing.T) { }, Port: 53, Networks: []net.Network{net.Network_TCP}, + Timeout: 10, FollowRedirect: true, UserLevel: 1, }, diff --git a/infra/conf/errors.generated.go b/infra/conf/errors.generated.go new file mode 100644 index 00000000..a80cc140 --- /dev/null +++ b/infra/conf/errors.generated.go @@ -0,0 +1,9 @@ +package conf + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/infra/conf/fakedns.go b/infra/conf/fakedns.go index 3aa20115..ca425457 100644 --- a/infra/conf/fakedns.go +++ b/infra/conf/fakedns.go @@ -1,12 +1,10 @@ package conf import ( - "context" "encoding/json" "strings" "github.com/xtls/xray-core/app/dns/fakedns" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/features/dns" ) @@ -20,18 +18,6 @@ type FakeDNSConfig struct { pools []*FakeDNSPoolElementConfig } -// MarshalJSON implements encoding/json.Marshaler.MarshalJSON -func (f *FakeDNSConfig) MarshalJSON() ([]byte, error) { - if (f.pool != nil) != (f.pools != nil) { - if f.pool != nil { - return json.Marshal(f.pool) - } else if f.pools != nil { - return json.Marshal(f.pools) - } - } - return nil, errors.New("unexpected config state") -} - // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (f *FakeDNSConfig) UnmarshalJSON(data []byte) error { var pool FakeDNSPoolElementConfig @@ -42,7 +28,7 @@ func (f *FakeDNSConfig) UnmarshalJSON(data []byte) error { case json.Unmarshal(data, &pools) == nil: f.pools = pools default: - return errors.New("invalid fakedns config") + return newError("invalid fakedns config") } return nil } @@ -65,7 +51,7 @@ func (f *FakeDNSConfig) Build() (*fakedns.FakeDnsPoolMulti, error) { return &fakeDNSPool, nil } - return nil, errors.New("no valid FakeDNS config") + return nil, newError("no valid FakeDNS config") } type FakeDNSPostProcessingStage struct{} @@ -136,7 +122,7 @@ func (FakeDNSPostProcessingStage) Process(config *Config) error { } } if !found { - errors.LogWarning(context.Background(), "Defined FakeDNS but haven't enabled FakeDNS destOverride at any inbound.") + newError("Defined FakeDNS but haven't enabled FakeDNS destOverride at any inbound.").AtWarning().WriteToLog() } } diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index 15f1bcff..60dfd5b8 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -1,138 +1,47 @@ package conf import ( - "encoding/base64" - "encoding/hex" "net" "strings" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" v2net "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/proxy/freedom" - "google.golang.org/protobuf/proto" ) type FreedomConfig struct { - DomainStrategy string `json:"domainStrategy"` - Redirect string `json:"redirect"` - UserLevel uint32 `json:"userLevel"` - Fragment *Fragment `json:"fragment"` - Noise *Noise `json:"noise"` - Noises []*Noise `json:"noises"` - ProxyProtocol uint32 `json:"proxyProtocol"` -} - -type Fragment struct { - Packets string `json:"packets"` - Length *Int32Range `json:"length"` - Interval *Int32Range `json:"interval"` -} - -type Noise struct { - Type string `json:"type"` - Packet string `json:"packet"` - Delay *Int32Range `json:"delay"` + DomainStrategy string `json:"domainStrategy"` + Timeout *uint32 `json:"timeout"` + Redirect string `json:"redirect"` + UserLevel uint32 `json:"userLevel"` } // Build implements Buildable func (c *FreedomConfig) Build() (proto.Message, error) { config := new(freedom.Config) + config.DomainStrategy = freedom.Config_AS_IS switch strings.ToLower(c.DomainStrategy) { - case "asis", "": - config.DomainStrategy = freedom.Config_AS_IS - case "useip": + case "useip", "use_ip", "use-ip": config.DomainStrategy = freedom.Config_USE_IP - case "useipv4": + case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": config.DomainStrategy = freedom.Config_USE_IP4 - case "useipv6": + case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": config.DomainStrategy = freedom.Config_USE_IP6 - case "useipv4v6": - config.DomainStrategy = freedom.Config_USE_IP46 - case "useipv6v4": - config.DomainStrategy = freedom.Config_USE_IP64 - case "forceip": - config.DomainStrategy = freedom.Config_FORCE_IP - case "forceipv4": - config.DomainStrategy = freedom.Config_FORCE_IP4 - case "forceipv6": - config.DomainStrategy = freedom.Config_FORCE_IP6 - case "forceipv4v6": - config.DomainStrategy = freedom.Config_FORCE_IP46 - case "forceipv6v4": - config.DomainStrategy = freedom.Config_FORCE_IP64 - default: - return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy) } - if c.Fragment != nil { - config.Fragment = new(freedom.Fragment) - - switch strings.ToLower(c.Fragment.Packets) { - case "tlshello": - // TLS Hello Fragmentation (into multiple handshake messages) - config.Fragment.PacketsFrom = 0 - config.Fragment.PacketsTo = 1 - case "": - // TCP Segmentation (all packets) - config.Fragment.PacketsFrom = 0 - config.Fragment.PacketsTo = 0 - default: - // TCP Segmentation (range) - from, to, err := ParseRangeString(c.Fragment.Packets) - if err != nil { - return nil, errors.New("Invalid PacketsFrom").Base(err) - } - config.Fragment.PacketsFrom = uint64(from) - config.Fragment.PacketsTo = uint64(to) - if config.Fragment.PacketsFrom == 0 { - return nil, errors.New("PacketsFrom can't be 0") - } - } - - { - if c.Fragment.Length == nil { - return nil, errors.New("Length can't be empty") - } - config.Fragment.LengthMin = uint64(c.Fragment.Length.From) - config.Fragment.LengthMax = uint64(c.Fragment.Length.To) - if config.Fragment.LengthMin == 0 { - return nil, errors.New("LengthMin can't be 0") - } - } - - { - if c.Fragment.Interval == nil { - return nil, errors.New("Interval can't be empty") - } - config.Fragment.IntervalMin = uint64(c.Fragment.Interval.From) - config.Fragment.IntervalMax = uint64(c.Fragment.Interval.To) - } + if c.Timeout != nil { + config.Timeout = *c.Timeout } - - if c.Noise != nil { - return nil, errors.PrintRemovedFeatureError("noise = { ... }", "noises = [ { ... } ]") - } - - if c.Noises != nil { - for _, n := range c.Noises { - NConfig, err := ParseNoise(n) - if err != nil { - return nil, err - } - config.Noises = append(config.Noises, NConfig) - } - } - config.UserLevel = c.UserLevel if len(c.Redirect) > 0 { host, portStr, err := net.SplitHostPort(c.Redirect) if err != nil { - return nil, errors.New("invalid redirect address: ", c.Redirect, ": ", err).Base(err) + return nil, newError("invalid redirect address: ", c.Redirect, ": ", err).Base(err) } port, err := v2net.PortFromString(portStr) if err != nil { - return nil, errors.New("invalid redirect port: ", c.Redirect, ": ", err).Base(err) + return nil, newError("invalid redirect port: ", c.Redirect, ": ", err).Base(err) } config.DestinationOverride = &freedom.DestinationOverride{ Server: &protocol.ServerEndpoint{ @@ -144,54 +53,5 @@ func (c *FreedomConfig) Build() (proto.Message, error) { config.DestinationOverride.Server.Address = v2net.NewIPOrDomain(v2net.ParseAddress(host)) } } - if c.ProxyProtocol > 0 && c.ProxyProtocol <= 2 { - config.ProxyProtocol = c.ProxyProtocol - } return config, nil } - -func ParseNoise(noise *Noise) (*freedom.Noise, error) { - var err error - NConfig := new(freedom.Noise) - noise.Packet = strings.TrimSpace(noise.Packet) - - switch noise.Type { - case "rand": - min, max, err := ParseRangeString(noise.Packet) - if err != nil { - return nil, errors.New("invalid value for rand Length").Base(err) - } - NConfig.LengthMin = uint64(min) - NConfig.LengthMax = uint64(max) - if NConfig.LengthMin == 0 { - return nil, errors.New("rand lengthMin or lengthMax cannot be 0") - } - - case "str": - // user input string - NConfig.Packet = []byte(noise.Packet) - - case "hex": - // user input hex - NConfig.Packet, err = hex.DecodeString(noise.Packet) - if err != nil { - return nil, errors.New("Invalid hex string").Base(err) - } - - case "base64": - // user input base64 - NConfig.Packet, err = base64.RawURLEncoding.DecodeString(strings.NewReplacer("+", "-", "/", "_", "=", "").Replace(noise.Packet)) - if err != nil { - return nil, errors.New("Invalid base64 string").Base(err) - } - - default: - return nil, errors.New("Invalid packet, only rand/str/hex/base64 are supported") - } - - if noise.Delay != nil { - NConfig.DelayMin = uint64(noise.Delay.From) - NConfig.DelayMax = uint64(noise.Delay.To) - } - return NConfig, nil -} diff --git a/infra/conf/freedom_test.go b/infra/conf/freedom_test.go index 3d9d142a..0d061558 100644 --- a/infra/conf/freedom_test.go +++ b/infra/conf/freedom_test.go @@ -18,12 +18,14 @@ func TestFreedomConfig(t *testing.T) { { Input: `{ "domainStrategy": "AsIs", + "timeout": 10, "redirect": "127.0.0.1:3366", "userLevel": 1 }`, Parser: loadJSON(creator), Output: &freedom.Config{ DomainStrategy: freedom.Config_AS_IS, + Timeout: 10, DestinationOverride: &freedom.DestinationOverride{ Server: &protocol.ServerEndpoint{ Address: &net.IPOrDomain{ diff --git a/infra/conf/general_test.go b/infra/conf/general_test.go index 4d23b3b5..1de1109c 100644 --- a/infra/conf/general_test.go +++ b/infra/conf/general_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common" . "github.com/xtls/xray-core/infra/conf" - "google.golang.org/protobuf/proto" ) func loadJSON(creator func() Buildable) func(string) (proto.Message, error) { diff --git a/infra/conf/grpc.go b/infra/conf/grpc.go index 429186b0..3813e40d 100644 --- a/infra/conf/grpc.go +++ b/infra/conf/grpc.go @@ -1,19 +1,17 @@ package conf import ( + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/transport/internet/grpc" - "google.golang.org/protobuf/proto" ) type GRPCConfig struct { - Authority string `json:"authority"` - ServiceName string `json:"serviceName"` + ServiceName string `json:"serviceName" ` MultiMode bool `json:"multiMode"` IdleTimeout int32 `json:"idle_timeout"` HealthCheckTimeout int32 `json:"health_check_timeout"` PermitWithoutStream bool `json:"permit_without_stream"` InitialWindowsSize int32 `json:"initial_windows_size"` - UserAgent string `json:"user_agent"` } func (g *GRPCConfig) Build() (proto.Message, error) { @@ -29,13 +27,11 @@ func (g *GRPCConfig) Build() (proto.Message, error) { } return &grpc.Config{ - Authority: g.Authority, ServiceName: g.ServiceName, MultiMode: g.MultiMode, IdleTimeout: g.IdleTimeout, HealthCheckTimeout: g.HealthCheckTimeout, PermitWithoutStream: g.PermitWithoutStream, InitialWindowsSize: g.InitialWindowsSize, - UserAgent: g.UserAgent, }, nil } diff --git a/infra/conf/http.go b/infra/conf/http.go index 7f59bd79..ddeaa69e 100644 --- a/infra/conf/http.go +++ b/infra/conf/http.go @@ -3,11 +3,10 @@ package conf import ( "encoding/json" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/proxy/http" - "google.golang.org/protobuf/proto" ) type HTTPAccount struct { @@ -23,6 +22,7 @@ func (v *HTTPAccount) Build() *http.Account { } type HTTPServerConfig struct { + Timeout uint32 `json:"timeout"` Accounts []*HTTPAccount `json:"accounts"` Transparent bool `json:"allowTransparent"` UserLevel uint32 `json:"userLevel"` @@ -30,6 +30,7 @@ type HTTPServerConfig struct { func (c *HTTPServerConfig) Build() (proto.Message, error) { config := &http.ServerConfig{ + Timeout: c.Timeout, AllowTransparent: c.Transparent, UserLevel: c.UserLevel, } @@ -66,11 +67,11 @@ func (v *HTTPClientConfig) Build() (proto.Message, error) { for _, rawUser := range serverConfig.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { - return nil, errors.New("failed to parse HTTP user").Base(err).AtError() + return nil, newError("failed to parse HTTP user").Base(err).AtError() } account := new(HTTPAccount) if err := json.Unmarshal(rawUser, account); err != nil { - return nil, errors.New("failed to parse HTTP account").Base(err).AtError() + return nil, newError("failed to parse HTTP account").Base(err).AtError() } user.Account = serial.ToTypedMessage(account.Build()) server.User = append(server.User, user) diff --git a/infra/conf/http_test.go b/infra/conf/http_test.go index 66cca396..110cac6b 100644 --- a/infra/conf/http_test.go +++ b/infra/conf/http_test.go @@ -15,6 +15,7 @@ func TestHTTPServerConfig(t *testing.T) { runMultiTestCase(t, []TestCase{ { Input: `{ + "timeout": 10, "accounts": [ { "user": "my-username", @@ -31,6 +32,7 @@ func TestHTTPServerConfig(t *testing.T) { }, AllowTransparent: true, UserLevel: 1, + Timeout: 10, }, }, }) diff --git a/infra/conf/lint.go b/infra/conf/lint.go index f8a6b38c..93da35d4 100644 --- a/infra/conf/lint.go +++ b/infra/conf/lint.go @@ -1,7 +1,5 @@ package conf -import "github.com/xtls/xray-core/common/errors" - type ConfigureFilePostProcessingStage interface { Process(conf *Config) error } @@ -18,7 +16,7 @@ func RegisterConfigureFilePostProcessingStage(name string, stage ConfigureFilePo func PostProcessConfigureFile(conf *Config) error { for k, v := range configureFilePostProcessingStages { if err := v.Process(conf); err != nil { - return errors.New("Rejected by Postprocessing Stage ", k).AtError().Base(err) + return newError("Rejected by Postprocessing Stage ", k).AtError().Base(err) } } return nil diff --git a/infra/conf/loader.go b/infra/conf/loader.go index 1dc2de23..51f268a3 100644 --- a/infra/conf/loader.go +++ b/infra/conf/loader.go @@ -3,8 +3,6 @@ package conf import ( "encoding/json" "strings" - - "github.com/xtls/xray-core/common/errors" ) type ConfigCreator func() interface{} @@ -13,7 +11,7 @@ type ConfigCreatorCache map[string]ConfigCreator func (v ConfigCreatorCache) RegisterCreator(id string, creator ConfigCreator) error { if _, found := v[id]; found { - return errors.New(id, " already registered.").AtError() + return newError(id, " already registered.").AtError() } v[id] = creator @@ -23,7 +21,7 @@ func (v ConfigCreatorCache) RegisterCreator(id string, creator ConfigCreator) er func (v ConfigCreatorCache) CreateConfig(id string) (interface{}, error) { creator, found := v[id] if !found { - return nil, errors.New("unknown config id: ", id) + return nil, newError("unknown config id: ", id) } return creator(), nil } @@ -61,7 +59,7 @@ func (v *JSONConfigLoader) Load(raw []byte) (interface{}, string, error) { } rawID, found := obj[v.idKey] if !found { - return nil, "", errors.New(v.idKey, " not found in JSON context").AtError() + return nil, "", newError(v.idKey, " not found in JSON context").AtError() } var id string if err := json.Unmarshal(rawID, &id); err != nil { diff --git a/infra/conf/log.go b/infra/conf/log.go index fee8f570..14f2a694 100644 --- a/infra/conf/log.go +++ b/infra/conf/log.go @@ -16,11 +16,10 @@ func DefaultLogConfig() *log.Config { } type LogConfig struct { - AccessLog string `json:"access"` - ErrorLog string `json:"error"` - LogLevel string `json:"loglevel"` - DNSLog bool `json:"dnsLog"` - MaskAddress string `json:"maskAddress"` + AccessLog string `json:"access"` + ErrorLog string `json:"error"` + LogLevel string `json:"loglevel"` + DNSLog bool `json:"dnsLog"` } func (v *LogConfig) Build() *log.Config { @@ -60,6 +59,5 @@ func (v *LogConfig) Build() *log.Config { default: config.ErrorLogLevel = clog.Severity_Warning } - config.MaskAddress = v.MaskAddress return config } diff --git a/infra/conf/loopback.go b/infra/conf/loopback.go index 87d349ce..b6d83178 100644 --- a/infra/conf/loopback.go +++ b/infra/conf/loopback.go @@ -1,8 +1,8 @@ package conf import ( + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/proxy/loopback" - "google.golang.org/protobuf/proto" ) type LoopbackConfig struct { diff --git a/infra/conf/metrics.go b/infra/conf/metrics.go index 75965206..2a0c8ef9 100644 --- a/infra/conf/metrics.go +++ b/infra/conf/metrics.go @@ -2,25 +2,18 @@ package conf import ( "github.com/xtls/xray-core/app/metrics" - "github.com/xtls/xray-core/common/errors" ) type MetricsConfig struct { - Tag string `json:"tag"` - Listen string `json:"listen"` + Tag string `json:"tag"` } func (c *MetricsConfig) Build() (*metrics.Config, error) { - if c.Listen == "" && c.Tag == "" { - return nil, errors.New("Metrics must have a tag or listen address.") - } - // If the tag is empty but have "listen" set a default "Metrics" for compatibility. if c.Tag == "" { - c.Tag = "Metrics" + return nil, newError("metrics tag can't be empty.") } return &metrics.Config{ - Tag: c.Tag, - Listen: c.Listen, + Tag: c.Tag, }, nil } diff --git a/infra/conf/mtproto.go b/infra/conf/mtproto.go new file mode 100644 index 00000000..88b02af5 --- /dev/null +++ b/infra/conf/mtproto.go @@ -0,0 +1,67 @@ +package conf + +import ( + "encoding/hex" + "encoding/json" + + "github.com/golang/protobuf/proto" + "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/proxy/mtproto" +) + +type MTProtoAccount struct { + Secret string `json:"secret"` +} + +// Build implements Buildable +func (a *MTProtoAccount) Build() (*mtproto.Account, error) { + if len(a.Secret) != 32 { + return nil, newError("MTProto secret must have 32 chars") + } + secret, err := hex.DecodeString(a.Secret) + if err != nil { + return nil, newError("failed to decode secret: ", a.Secret).Base(err) + } + return &mtproto.Account{ + Secret: secret, + }, nil +} + +type MTProtoServerConfig struct { + Users []json.RawMessage `json:"users"` +} + +func (c *MTProtoServerConfig) Build() (proto.Message, error) { + config := &mtproto.ServerConfig{} + + if len(c.Users) == 0 { + return nil, newError("zero MTProto users configured.") + } + config.User = make([]*protocol.User, len(c.Users)) + for idx, rawData := range c.Users { + user := new(protocol.User) + if err := json.Unmarshal(rawData, user); err != nil { + return nil, newError("invalid MTProto user").Base(err) + } + account := new(MTProtoAccount) + if err := json.Unmarshal(rawData, account); err != nil { + return nil, newError("invalid MTProto user").Base(err) + } + accountProto, err := account.Build() + if err != nil { + return nil, newError("failed to parse MTProto user").Base(err) + } + user.Account = serial.ToTypedMessage(accountProto) + config.User[idx] = user + } + + return config, nil +} + +type MTProtoClientConfig struct{} + +func (c *MTProtoClientConfig) Build() (proto.Message, error) { + config := new(mtproto.ClientConfig) + return config, nil +} diff --git a/infra/conf/mtproto_test.go b/infra/conf/mtproto_test.go new file mode 100644 index 00000000..f44cb19a --- /dev/null +++ b/infra/conf/mtproto_test.go @@ -0,0 +1,40 @@ +package conf_test + +import ( + "testing" + + "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/serial" + . "github.com/xtls/xray-core/infra/conf" + "github.com/xtls/xray-core/proxy/mtproto" +) + +func TestMTProtoServerConfig(t *testing.T) { + creator := func() Buildable { + return new(MTProtoServerConfig) + } + + runMultiTestCase(t, []TestCase{ + { + Input: `{ + "users": [{ + "email": "love@example.com", + "level": 1, + "secret": "b0cbcef5a486d9636472ac27f8e11a9d" + }] + }`, + Parser: loadJSON(creator), + Output: &mtproto.ServerConfig{ + User: []*protocol.User{ + { + Email: "love@example.com", + Level: 1, + Account: serial.ToTypedMessage(&mtproto.Account{ + Secret: []byte{176, 203, 206, 245, 164, 134, 217, 99, 100, 114, 172, 39, 248, 225, 26, 157}, + }), + }, + }, + }, + }, + }) +} diff --git a/infra/conf/observatory.go b/infra/conf/observatory.go index 62d6aaba..25a4d52c 100644 --- a/infra/conf/observatory.go +++ b/infra/conf/observatory.go @@ -1,11 +1,8 @@ package conf import ( - "google.golang.org/protobuf/proto" - + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/observatory" - "github.com/xtls/xray-core/app/observatory/burst" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/infra/conf/cfgcommon/duration" ) @@ -19,20 +16,3 @@ type ObservatoryConfig struct { func (o *ObservatoryConfig) Build() (proto.Message, error) { return &observatory.Config{SubjectSelector: o.SubjectSelector, ProbeUrl: o.ProbeURL, ProbeInterval: int64(o.ProbeInterval), EnableConcurrency: o.EnableConcurrency}, nil } - -type BurstObservatoryConfig struct { - SubjectSelector []string `json:"subjectSelector"` - // health check settings - HealthCheck *healthCheckSettings `json:"pingConfig,omitempty"` -} - -func (b BurstObservatoryConfig) Build() (proto.Message, error) { - if b.HealthCheck == nil { - return nil, errors.New("BurstObservatory requires a valid pingConfig") - } - if result, err := b.HealthCheck.Build(); err == nil { - return &burst.Config{SubjectSelector: b.SubjectSelector, PingConfig: result.(*burst.HealthPingConfig)}, nil - } else { - return nil, err - } -} diff --git a/infra/conf/policy.go b/infra/conf/policy.go index 1182766c..5fbf01e6 100644 --- a/infra/conf/policy.go +++ b/infra/conf/policy.go @@ -11,7 +11,6 @@ type Policy struct { DownlinkOnly *uint32 `json:"downlinkOnly"` StatsUserUplink bool `json:"statsUserUplink"` StatsUserDownlink bool `json:"statsUserDownlink"` - StatsUserOnline bool `json:"statsUserOnline"` BufferSize *int32 `json:"bufferSize"` } @@ -35,7 +34,6 @@ func (t *Policy) Build() (*policy.Policy, error) { Stats: &policy.Policy_Stats{ UserUplink: t.StatsUserUplink, UserDownlink: t.StatsUserDownlink, - UserOnline: t.StatsUserOnline, }, } diff --git a/infra/conf/reverse.go b/infra/conf/reverse.go index f44c9992..1b42a63a 100644 --- a/infra/conf/reverse.go +++ b/infra/conf/reverse.go @@ -1,8 +1,8 @@ package conf import ( + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/reverse" - "google.golang.org/protobuf/proto" ) type BridgeConfig struct { diff --git a/infra/conf/router.go b/infra/conf/router.go index 2065f96b..e10317bd 100644 --- a/infra/conf/router.go +++ b/infra/conf/router.go @@ -6,14 +6,17 @@ import ( "strconv" "strings" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/router" - "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/serial" - "google.golang.org/protobuf/proto" ) +type RouterRulesConfig struct { + RuleList []json.RawMessage `json:"rules"` + DomainStrategy string `json:"domainStrategy"` +} + // StrategyConfig represents a strategy config type StrategyConfig struct { Type string `json:"type"` @@ -21,59 +24,41 @@ type StrategyConfig struct { } type BalancingRule struct { - Tag string `json:"tag"` - Selectors StringList `json:"selector"` - Strategy StrategyConfig `json:"strategy"` - FallbackTag string `json:"fallbackTag"` + Tag string `json:"tag"` + Selectors StringList `json:"selector"` + Strategy StrategyConfig `json:"strategy"` } -// Build builds the balancing rule func (r *BalancingRule) Build() (*router.BalancingRule, error) { if r.Tag == "" { - return nil, errors.New("empty balancer tag") + return nil, newError("empty balancer tag") } if len(r.Selectors) == 0 { - return nil, errors.New("empty selector list") + return nil, newError("empty selector list") } - r.Strategy.Type = strings.ToLower(r.Strategy.Type) - switch r.Strategy.Type { - case "": - r.Strategy.Type = strategyRandom - case strategyRandom, strategyLeastLoad, strategyLeastPing, strategyRoundRobin: + var strategy string + switch strings.ToLower(r.Strategy.Type) { + case strategyRandom, "": + strategy = strategyRandom + case strategyLeastPing: + strategy = "leastPing" default: - return nil, errors.New("unknown balancing strategy: " + r.Strategy.Type) - } - - settings := []byte("{}") - if r.Strategy.Settings != nil { - settings = ([]byte)(*r.Strategy.Settings) - } - rawConfig, err := strategyConfigLoader.LoadWithID(settings, r.Strategy.Type) - if err != nil { - return nil, errors.New("failed to parse to strategy config.").Base(err) - } - var ts proto.Message - if builder, ok := rawConfig.(Buildable); ok { - ts, err = builder.Build() - if err != nil { - return nil, err - } + return nil, newError("unknown balancing strategy: " + r.Strategy.Type) } return &router.BalancingRule{ - Strategy: r.Strategy.Type, - StrategySettings: serial.ToTypedMessage(ts), - FallbackTag: r.FallbackTag, - OutboundSelector: r.Selectors, Tag: r.Tag, + OutboundSelector: []string(r.Selectors), + Strategy: strategy, }, nil } type RouterConfig struct { - RuleList []json.RawMessage `json:"rules"` - DomainStrategy *string `json:"domainStrategy"` - Balancers []*BalancingRule `json:"balancers"` + Settings *RouterRulesConfig `json:"settings"` // Deprecated + RuleList []json.RawMessage `json:"rules"` + DomainStrategy *string `json:"domainStrategy"` + Balancers []*BalancingRule `json:"balancers"` DomainMatcher string `json:"domainMatcher"` } @@ -82,6 +67,8 @@ func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy { ds := "" if c.DomainStrategy != nil { ds = *c.DomainStrategy + } else if c.Settings != nil { + ds = c.Settings.DomainStrategy } switch strings.ToLower(ds) { @@ -103,6 +90,10 @@ func (c *RouterConfig) Build() (*router.Config, error) { var rawRuleList []json.RawMessage if c != nil { rawRuleList = c.RuleList + if c.Settings != nil { + c.RuleList = append(c.RuleList, c.Settings.RuleList...) + rawRuleList = c.RuleList + } } for _, rawRule := range rawRuleList { @@ -128,7 +119,6 @@ func (c *RouterConfig) Build() (*router.Config, error) { } type RouterRule struct { - RuleTag string `json:"ruleTag"` Type string `json:"type"` OutboundTag string `json:"outboundTag"` BalancerTag string `json:"balancerTag"` @@ -152,12 +142,12 @@ func ParseIP(s string) (*router.CIDR, error) { if len(mask) > 0 { bits64, err := strconv.ParseUint(mask, 10, 32) if err != nil { - return nil, errors.New("invalid network mask for router: ", mask).Base(err) + return nil, newError("invalid network mask for router: ", mask).Base(err) } bits = uint32(bits64) } if bits > 32 { - return nil, errors.New("invalid network mask for router: ", bits) + return nil, newError("invalid network mask for router: ", bits) } return &router.CIDR{ Ip: []byte(ip.IP()), @@ -168,19 +158,19 @@ func ParseIP(s string) (*router.CIDR, error) { if len(mask) > 0 { bits64, err := strconv.ParseUint(mask, 10, 32) if err != nil { - return nil, errors.New("invalid network mask for router: ", mask).Base(err) + return nil, newError("invalid network mask for router: ", mask).Base(err) } bits = uint32(bits64) } if bits > 128 { - return nil, errors.New("invalid network mask for router: ", bits) + return nil, newError("invalid network mask for router: ", bits) } return &router.CIDR{ Ip: []byte(ip.IP()), Prefix: bits, }, nil default: - return nil, errors.New("unsupported address for router: ", s) + return nil, newError("unsupported address for router: ", s) } } @@ -198,10 +188,10 @@ func loadFile(file string) ([]byte, error) { if FileCache[file] == nil { bs, err := filesystem.ReadAsset(file) if err != nil { - return nil, errors.New("failed to open file: ", file).Base(err) + return nil, newError("failed to open file: ", file).Base(err) } if len(bs) == 0 { - return nil, errors.New("empty file: ", file) + return nil, newError("empty file: ", file) } // Do not cache file, may save RAM when there // are many files, but consume CPU each time. @@ -216,15 +206,15 @@ func loadIP(file, code string) ([]*router.CIDR, error) { if IPCache[index] == nil { bs, err := loadFile(file) if err != nil { - return nil, errors.New("failed to load file: ", file).Base(err) + return nil, newError("failed to load file: ", file).Base(err) } bs = find(bs, []byte(code)) if bs == nil { - return nil, errors.New("code not found in ", file, ": ", code) + return nil, newError("code not found in ", file, ": ", code) } var geoip router.GeoIP if err := proto.Unmarshal(bs, &geoip); err != nil { - return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err) + return nil, newError("error unmarshal IP in ", file, ": ", code).Base(err) } defer runtime.GC() // or debug.FreeOSMemory() return geoip.Cidr, nil // do not cache geoip @@ -238,15 +228,15 @@ func loadSite(file, code string) ([]*router.Domain, error) { if SiteCache[index] == nil { bs, err := loadFile(file) if err != nil { - return nil, errors.New("failed to load file: ", file).Base(err) + return nil, newError("failed to load file: ", file).Base(err) } bs = find(bs, []byte(code)) if bs == nil { - return nil, errors.New("list not found in ", file, ": ", code) + return nil, newError("list not found in ", file, ": ", code) } var geosite router.GeoSite if err := proto.Unmarshal(bs, &geosite); err != nil { - return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err) + return nil, newError("error unmarshal Site in ", file, ": ", code).Base(err) } defer runtime.GC() // or debug.FreeOSMemory() return geosite.Domain, nil // do not cache geosite @@ -255,23 +245,6 @@ func loadSite(file, code string) ([]*router.Domain, error) { return SiteCache[index].Domain, nil } -func DecodeVarint(buf []byte) (x uint64, n int) { - for shift := uint(0); shift < 64; shift += 7 { - if n >= len(buf) { - return 0, 0 - } - b := uint64(buf[n]) - n++ - x |= (b & 0x7F) << shift - if (b & 0x80) == 0 { - return x, n - } - } - - // The number is too large to represent in a 64-bit value. - return 0, 0 -} - func find(data, code []byte) []byte { codeL := len(code) if codeL == 0 { @@ -282,7 +255,7 @@ func find(data, code []byte) []byte { if dataL < 2 { return nil } - x, y := DecodeVarint(data[1:]) + x, y := proto.DecodeVarint(data[1:]) if x == 0 && y == 0 { return nil } @@ -350,7 +323,7 @@ func parseAttrs(attrs []string) *AttributeList { func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) { parts := strings.Split(siteWithAttr, "@") if len(parts) == 0 { - return nil, errors.New("empty site") + return nil, newError("empty site") } country := strings.ToUpper(parts[0]) attrs := parseAttrs(parts[1:]) @@ -378,7 +351,7 @@ func parseDomainRule(domain string) ([]*router.Domain, error) { country := strings.ToUpper(domain[8:]) domains, err := loadGeositeWithAttr("geosite.dat", country) if err != nil { - return nil, errors.New("failed to load geosite: ", country).Base(err) + return nil, newError("failed to load geosite: ", country).Base(err) } return domains, nil } @@ -396,13 +369,13 @@ func parseDomainRule(domain string) ([]*router.Domain, error) { if isExtDatFile != 0 { kv := strings.Split(domain[isExtDatFile:], ":") if len(kv) != 2 { - return nil, errors.New("invalid external resource: ", domain) + return nil, newError("invalid external resource: ", domain) } filename := kv[0] country := kv[1] domains, err := loadGeositeWithAttr(filename, country) if err != nil { - return nil, errors.New("failed to load external sites: ", country, " from ", filename).Base(err) + return nil, newError("failed to load external sites: ", country, " from ", filename).Base(err) } return domains, nil } @@ -433,7 +406,7 @@ func parseDomainRule(domain string) ([]*router.Domain, error) { case !strings.Contains(substr, "."): domainRule.Value = "^[^.]*" + substr + "[^.]*$" default: - return nil, errors.New("substr in dotless rule should not contain a dot: ", substr) + return nil, newError("substr in dotless rule should not contain a dot: ", substr) } default: @@ -456,11 +429,11 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) { isReverseMatch = true } if len(country) == 0 { - return nil, errors.New("empty country name in rule") + return nil, newError("empty country name in rule") } geoip, err := loadGeoIP(strings.ToUpper(country)) if err != nil { - return nil, errors.New("failed to load GeoIP: ", country).Base(err) + return nil, newError("failed to load GeoIP: ", country).Base(err) } geoipList = append(geoipList, &router.GeoIP{ @@ -484,13 +457,13 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) { if isExtDatFile != 0 { kv := strings.Split(ip[isExtDatFile:], ":") if len(kv) != 2 { - return nil, errors.New("invalid external resource: ", ip) + return nil, newError("invalid external resource: ", ip) } filename := kv[0] country := kv[1] if len(filename) == 0 || len(country) == 0 { - return nil, errors.New("empty filename or empty country in rule") + return nil, newError("empty filename or empty country in rule") } isReverseMatch := false @@ -500,7 +473,7 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) { } geoip, err := loadIP(filename, strings.ToUpper(country)) if err != nil { - return nil, errors.New("failed to load IPs: ", country, " from ", filename).Base(err) + return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err) } geoipList = append(geoipList, &router.GeoIP{ @@ -514,7 +487,7 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) { ipRule, err := ParseIP(ip) if err != nil { - return nil, errors.New("invalid IP: ", ip).Base(err) + return nil, newError("invalid IP: ", ip).Base(err) } customCidrs = append(customCidrs, ipRule) } @@ -531,17 +504,17 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) { func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { type RawFieldRule struct { RouterRule - Domain *StringList `json:"domain"` - Domains *StringList `json:"domains"` - IP *StringList `json:"ip"` - Port *PortList `json:"port"` - Network *NetworkList `json:"network"` - SourceIP *StringList `json:"source"` - SourcePort *PortList `json:"sourcePort"` - User *StringList `json:"user"` - InboundTag *StringList `json:"inboundTag"` - Protocols *StringList `json:"protocol"` - Attributes map[string]string `json:"attrs"` + Domain *StringList `json:"domain"` + Domains *StringList `json:"domains"` + IP *StringList `json:"ip"` + Port *PortList `json:"port"` + Network *NetworkList `json:"network"` + SourceIP *StringList `json:"source"` + SourcePort *PortList `json:"sourcePort"` + User *StringList `json:"user"` + InboundTag *StringList `json:"inboundTag"` + Protocols *StringList `json:"protocol"` + Attributes string `json:"attrs"` } rawFieldRule := new(RawFieldRule) err := json.Unmarshal(msg, rawFieldRule) @@ -550,7 +523,6 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { } rule := new(router.RoutingRule) - rule.RuleTag = rawFieldRule.RuleTag switch { case len(rawFieldRule.OutboundTag) > 0: rule.TargetTag = &router.RoutingRule_Tag{ @@ -561,7 +533,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { BalancingTag: rawFieldRule.BalancerTag, } default: - return nil, errors.New("neither outboundTag nor balancerTag is specified in routing rule") + return nil, newError("neither outboundTag nor balancerTag is specified in routing rule") } if rawFieldRule.DomainMatcher != "" { @@ -572,7 +544,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { for _, domain := range *rawFieldRule.Domain { rules, err := parseDomainRule(domain) if err != nil { - return nil, errors.New("failed to parse domain rule: ", domain).Base(err) + return nil, newError("failed to parse domain rule: ", domain).Base(err) } rule.Domain = append(rule.Domain, rules...) } @@ -582,7 +554,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { for _, domain := range *rawFieldRule.Domains { rules, err := parseDomainRule(domain) if err != nil { - return nil, errors.New("failed to parse domain rule: ", domain).Base(err) + return nil, newError("failed to parse domain rule: ", domain).Base(err) } rule.Domain = append(rule.Domain, rules...) } @@ -645,14 +617,64 @@ func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) { rawRule := new(RouterRule) err := json.Unmarshal(msg, rawRule) if err != nil { - return nil, errors.New("invalid router rule").Base(err) + return nil, newError("invalid router rule").Base(err) } - if rawRule.Type == "" || strings.EqualFold(rawRule.Type, "field") { + if strings.EqualFold(rawRule.Type, "field") { fieldrule, err := parseFieldRule(msg) if err != nil { - return nil, errors.New("invalid field rule").Base(err) + return nil, newError("invalid field rule").Base(err) } return fieldrule, nil } - return nil, errors.New("unknown router rule type: ", rawRule.Type) + if strings.EqualFold(rawRule.Type, "chinaip") { + chinaiprule, err := parseChinaIPRule(msg) + if err != nil { + return nil, newError("invalid chinaip rule").Base(err) + } + return chinaiprule, nil + } + if strings.EqualFold(rawRule.Type, "chinasites") { + chinasitesrule, err := parseChinaSitesRule(msg) + if err != nil { + return nil, newError("invalid chinasites rule").Base(err) + } + return chinasitesrule, nil + } + return nil, newError("unknown router rule type: ", rawRule.Type) +} + +func parseChinaIPRule(data []byte) (*router.RoutingRule, error) { + rawRule := new(RouterRule) + err := json.Unmarshal(data, rawRule) + if err != nil { + return nil, newError("invalid router rule").Base(err) + } + chinaIPs, err := loadGeoIP("CN") + if err != nil { + return nil, newError("failed to load geoip:cn").Base(err) + } + return &router.RoutingRule{ + TargetTag: &router.RoutingRule_Tag{ + Tag: rawRule.OutboundTag, + }, + Cidr: chinaIPs, + }, nil +} + +func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) { + rawRule := new(RouterRule) + err := json.Unmarshal(data, rawRule) + if err != nil { + return nil, newError("invalid router rule").Base(err).AtError() + } + domains, err := loadGeositeWithAttr("geosite.dat", "CN") + if err != nil { + return nil, newError("failed to load geosite:cn.").Base(err) + } + return &router.RoutingRule{ + TargetTag: &router.RoutingRule_Tag{ + Tag: rawRule.OutboundTag, + }, + Domain: domains, + }, nil } diff --git a/infra/conf/router_strategy.go b/infra/conf/router_strategy.go index 464cbcfb..b8536330 100644 --- a/infra/conf/router_strategy.go +++ b/infra/conf/router_strategy.go @@ -1,102 +1,6 @@ package conf -import ( - "google.golang.org/protobuf/proto" - "strings" - - "github.com/xtls/xray-core/app/observatory/burst" - "github.com/xtls/xray-core/app/router" - "github.com/xtls/xray-core/infra/conf/cfgcommon/duration" -) - const ( - strategyRandom string = "random" - strategyLeastPing string = "leastping" - strategyRoundRobin string = "roundrobin" - strategyLeastLoad string = "leastload" + strategyRandom string = "random" + strategyLeastPing string = "leastping" ) - -var ( - strategyConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{ - strategyRandom: func() interface{} { return new(strategyEmptyConfig) }, - strategyLeastPing: func() interface{} { return new(strategyEmptyConfig) }, - strategyRoundRobin: func() interface{} { return new(strategyEmptyConfig) }, - strategyLeastLoad: func() interface{} { return new(strategyLeastLoadConfig) }, - }, "type", "settings") -) - -type strategyEmptyConfig struct { -} - -func (v *strategyEmptyConfig) Build() (proto.Message, error) { - return nil, nil -} - -type strategyLeastLoadConfig struct { - // weight settings - Costs []*router.StrategyWeight `json:"costs,omitempty"` - // ping rtt baselines - Baselines []duration.Duration `json:"baselines,omitempty"` - // expected nodes count to select - Expected int32 `json:"expected,omitempty"` - // max acceptable rtt, filter away high delay nodes. default 0 - MaxRTT duration.Duration `json:"maxRTT,omitempty"` - // acceptable failure rate - Tolerance float64 `json:"tolerance,omitempty"` -} - -// healthCheckSettings holds settings for health Checker -type healthCheckSettings struct { - Destination string `json:"destination"` - Connectivity string `json:"connectivity"` - Interval duration.Duration `json:"interval"` - SamplingCount int `json:"sampling"` - Timeout duration.Duration `json:"timeout"` - HttpMethod string `json:"httpMethod"` -} - -func (h healthCheckSettings) Build() (proto.Message, error) { - var httpMethod string - if h.HttpMethod == "" { - httpMethod = "HEAD" - } else { - httpMethod = strings.TrimSpace(h.HttpMethod) - } - return &burst.HealthPingConfig{ - Destination: h.Destination, - Connectivity: h.Connectivity, - Interval: int64(h.Interval), - Timeout: int64(h.Timeout), - SamplingCount: int32(h.SamplingCount), - HttpMethod: httpMethod, - }, nil -} - -// Build implements Buildable. -func (v *strategyLeastLoadConfig) Build() (proto.Message, error) { - config := &router.StrategyLeastLoadConfig{} - config.Costs = v.Costs - config.Tolerance = float32(v.Tolerance) - if config.Tolerance < 0 { - config.Tolerance = 0 - } - if config.Tolerance > 1 { - config.Tolerance = 1 - } - config.Expected = v.Expected - if config.Expected < 0 { - config.Expected = 0 - } - config.MaxRTT = int64(v.MaxRTT) - if config.MaxRTT < 0 { - config.MaxRTT = 0 - } - config.Baselines = make([]int64, 0) - for _, b := range v.Baselines { - if b <= 0 { - continue - } - config.Baselines = append(config.Baselines, int64(b)) - } - return config, nil -} diff --git a/infra/conf/router_test.go b/infra/conf/router_test.go index 35c68c68..98e31b3a 100644 --- a/infra/conf/router_test.go +++ b/infra/conf/router_test.go @@ -2,61 +2,35 @@ package conf_test import ( "encoding/json" - "fmt" "os" "path/filepath" "testing" - "time" _ "unsafe" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/platform/filesystem" - "github.com/xtls/xray-core/common/serial" . "github.com/xtls/xray-core/infra/conf" - "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) +func init() { + wd, err := os.Getwd() + common.Must(err) + + if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) { + common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "resources", "geoip.dat"))) } - return path, nil + os.Setenv("xray.location.asset", wd) } func TestToCidrList(t *testing.T) { - tempDir, err := os.MkdirTemp("", "test-") - if err != nil { - t.Fatalf("can't create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) + t.Log(os.Getenv("xray.location.asset")) - geoipPath, err := getAssetPath("geoip.dat") - if err != nil { - t.Fatal(err) - } - - common.Must(filesystem.CopyFile(filepath.Join(tempDir, "geoip.dat"), geoipPath)) - common.Must(filesystem.CopyFile(filepath.Join(tempDir, "geoiptestrouter.dat"), geoipPath)) - - os.Setenv("xray.location.asset", tempDir) - defer os.Unsetenv("xray.location.asset") + common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoiptestrouter.dat"), "geoip.dat")) ips := StringList([]string{ "geoip:us", @@ -68,7 +42,7 @@ func TestToCidrList(t *testing.T) { "ext-ip:geoiptestrouter.dat:!ca", }) - _, err = ToCidrList(ips) + _, err := ToCidrList(ips) if err != nil { t.Fatalf("Failed to parse geoip list, got %s", err) } @@ -88,66 +62,40 @@ func TestRouterConfig(t *testing.T) { runMultiTestCase(t, []TestCase{ { Input: `{ - "domainStrategy": "AsIs", - "rules": [ - { - "type": "field", - "domain": [ - "baidu.com", - "qq.com" - ], - "outboundTag": "direct" - }, - { - "type": "field", - "ip": [ - "10.0.0.0/8", - "::1/128" - ], - "outboundTag": "test" - },{ - "type": "field", - "port": "53, 443, 1000-2000", - "outboundTag": "test" - },{ - "type": "field", - "port": 123, - "outboundTag": "test" - } - ], + "strategy": "rules", + "settings": { + "domainStrategy": "AsIs", + "rules": [ + { + "type": "field", + "domain": [ + "baidu.com", + "qq.com" + ], + "outboundTag": "direct" + }, + { + "type": "field", + "ip": [ + "10.0.0.0/8", + "::1/128" + ], + "outboundTag": "test" + },{ + "type": "field", + "port": "53, 443, 1000-2000", + "outboundTag": "test" + },{ + "type": "field", + "port": 123, + "outboundTag": "test" + } + ] + }, "balancers": [ { "tag": "b1", - "selector": ["test"], - "fallbackTag": "fall" - }, - { - "tag": "b2", - "selector": ["test"], - "strategy": { - "type": "leastload", - "settings": { - "healthCheck": { - "interval": "5m0s", - "sampling": 2, - "timeout": "5s", - "destination": "dest", - "connectivity": "conn" - }, - "costs": [ - { - "regexp": true, - "match": "\\d+(\\.\\d+)", - "value": 5 - } - ], - "baselines": ["400ms", "600ms"], - "expected": 6, - "maxRTT": "1000ms", - "tolerance": 0.5 - } - }, - "fallbackTag": "fall" + "selector": ["test"] } ] }`, @@ -159,29 +107,6 @@ func TestRouterConfig(t *testing.T) { Tag: "b1", OutboundSelector: []string{"test"}, Strategy: "random", - FallbackTag: "fall", - }, - { - Tag: "b2", - OutboundSelector: []string{"test"}, - Strategy: "leastload", - StrategySettings: serial.ToTypedMessage(&router.StrategyLeastLoadConfig{ - Costs: []*router.StrategyWeight{ - { - Regexp: true, - Match: "\\d+(\\.\\d+)", - Value: 5, - }, - }, - Baselines: []int64{ - int64(time.Duration(400) * time.Millisecond), - int64(time.Duration(600) * time.Millisecond), - }, - Expected: 6, - MaxRTT: int64(time.Duration(1000) * time.Millisecond), - Tolerance: 0.5, - }), - FallbackTag: "fall", }, }, Rule: []*router.RoutingRule{ @@ -246,25 +171,28 @@ func TestRouterConfig(t *testing.T) { }, { Input: `{ - "domainStrategy": "IPIfNonMatch", - "rules": [ - { - "type": "field", - "domain": [ - "baidu.com", - "qq.com" - ], - "outboundTag": "direct" - }, - { - "type": "field", - "ip": [ - "10.0.0.0/8", - "::1/128" - ], - "outboundTag": "test" - } - ] + "strategy": "rules", + "settings": { + "domainStrategy": "IPIfNonMatch", + "rules": [ + { + "type": "field", + "domain": [ + "baidu.com", + "qq.com" + ], + "outboundTag": "direct" + }, + { + "type": "field", + "ip": [ + "10.0.0.0/8", + "::1/128" + ], + "outboundTag": "test" + } + ] + } }`, Parser: createParser(), Output: &router.Config{ @@ -307,5 +235,68 @@ func TestRouterConfig(t *testing.T) { }, }, }, + { + Input: `{ + "domainStrategy": "AsIs", + "rules": [ + { + "type": "field", + "domain": [ + "baidu.com", + "qq.com" + ], + "outboundTag": "direct" + }, + { + "type": "field", + "ip": [ + "10.0.0.0/8", + "::1/128" + ], + "outboundTag": "test" + } + ] + }`, + Parser: createParser(), + Output: &router.Config{ + DomainStrategy: router.Config_AsIs, + Rule: []*router.RoutingRule{ + { + Domain: []*router.Domain{ + { + Type: router.Domain_Plain, + Value: "baidu.com", + }, + { + Type: router.Domain_Plain, + Value: "qq.com", + }, + }, + TargetTag: &router.RoutingRule_Tag{ + Tag: "direct", + }, + }, + { + Geoip: []*router.GeoIP{ + { + Cidr: []*router.CIDR{ + { + Ip: []byte{10, 0, 0, 0}, + Prefix: 8, + }, + { + Ip: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Prefix: 128, + }, + }, + }, + }, + TargetTag: &router.RoutingRule_Tag{ + Tag: "test", + }, + }, + }, + }, + }, }) } diff --git a/infra/conf/serial/builder.go b/infra/conf/serial/builder.go index 3ae98025..443dbdb0 100644 --- a/infra/conf/serial/builder.go +++ b/infra/conf/serial/builder.go @@ -1,55 +1,32 @@ package serial import ( - "context" "io" - "github.com/xtls/xray-core/common/errors" - creflect "github.com/xtls/xray-core/common/reflect" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/infra/conf" "github.com/xtls/xray-core/main/confloader" ) -func MergeConfigFromFiles(files []*core.ConfigSource) (string, error) { - c, err := mergeConfigs(files) - if err != nil { - return "", err - } - - if j, ok := creflect.MarshalToJson(c, true); ok { - return j, nil - } - return "", errors.New("marshal to json failed.").AtError() -} - -func mergeConfigs(files []*core.ConfigSource) (*conf.Config, error) { +func BuildConfig(files []string, formats []string) (*core.Config, error) { cf := &conf.Config{} for i, file := range files { - errors.LogInfo(context.Background(), "Reading config: ", file) - r, err := confloader.LoadConfig(file.Name) + newError("Reading config: ", file).AtInfo().WriteToLog() + r, err := confloader.LoadConfig(file) if err != nil { - return nil, errors.New("failed to read config: ", file).Base(err) + return nil, newError("failed to read config: ", file).Base(err) } - c, err := ReaderDecoderByFormat[file.Format](r) + c, err := ReaderDecoderByFormat[formats[i]](r) if err != nil { - return nil, errors.New("failed to decode config: ", file).Base(err) + return nil, newError("failed to decode config: ", file).Base(err) } if i == 0 { *cf = *c continue } - cf.Override(c, file.Name) + cf.Override(c, file) } - return cf, nil -} - -func BuildConfig(files []*core.ConfigSource) (*core.Config, error) { - config, err := mergeConfigs(files) - if err != nil { - return nil, err - } - return config.Build() + return cf.Build() } type readerDecoder func(io.Reader) (*conf.Config, error) @@ -62,5 +39,4 @@ func init() { ReaderDecoderByFormat["toml"] = DecodeTOMLConfig core.ConfigBuilderForFiles = BuildConfig - core.ConfigMergedFormFiles = MergeConfigFromFiles } diff --git a/infra/conf/serial/errors.generated.go b/infra/conf/serial/errors.generated.go new file mode 100644 index 00000000..e78fc61e --- /dev/null +++ b/infra/conf/serial/errors.generated.go @@ -0,0 +1,9 @@ +package serial + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/infra/conf/serial/loader.go b/infra/conf/serial/loader.go index ef9963df..e3235675 100644 --- a/infra/conf/serial/loader.go +++ b/infra/conf/serial/loader.go @@ -61,9 +61,9 @@ func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) { pos = findOffset(jsonContent.Bytes(), int(tErr.Offset)) } if pos != nil { - return nil, errors.New("failed to read config file at line ", pos.line, " char ", pos.char).Base(err) + return nil, newError("failed to read config file at line ", pos.line, " char ", pos.char).Base(err) } - return nil, errors.New("failed to read config file").Base(err) + return nil, newError("failed to read config file").Base(err) } return jsonConfig, nil @@ -77,7 +77,7 @@ func LoadJSONConfig(reader io.Reader) (*core.Config, error) { pbConfig, err := jsonConfig.Build() if err != nil { - return nil, errors.New("failed to parse json config").Base(err) + return nil, newError("failed to parse json config").Base(err) } return pbConfig, nil @@ -88,17 +88,17 @@ func LoadJSONConfig(reader io.Reader) (*core.Config, error) { func DecodeTOMLConfig(reader io.Reader) (*conf.Config, error) { tomlFile, err := io.ReadAll(reader) if err != nil { - return nil, errors.New("failed to read config file").Base(err) + return nil, newError("failed to read config file").Base(err) } configMap := make(map[string]interface{}) if err := toml.Unmarshal(tomlFile, &configMap); err != nil { - return nil, errors.New("failed to convert toml to map").Base(err) + return nil, newError("failed to convert toml to map").Base(err) } jsonFile, err := json.Marshal(&configMap) if err != nil { - return nil, errors.New("failed to convert map to json").Base(err) + return nil, newError("failed to convert map to json").Base(err) } return DecodeJSONConfig(bytes.NewReader(jsonFile)) @@ -112,7 +112,7 @@ func LoadTOMLConfig(reader io.Reader) (*core.Config, error) { pbConfig, err := tomlConfig.Build() if err != nil { - return nil, errors.New("failed to parse toml config").Base(err) + return nil, newError("failed to parse toml config").Base(err) } return pbConfig, nil @@ -123,12 +123,12 @@ func LoadTOMLConfig(reader io.Reader) (*core.Config, error) { func DecodeYAMLConfig(reader io.Reader) (*conf.Config, error) { yamlFile, err := io.ReadAll(reader) if err != nil { - return nil, errors.New("failed to read config file").Base(err) + return nil, newError("failed to read config file").Base(err) } jsonFile, err := yaml.YAMLToJSON(yamlFile) if err != nil { - return nil, errors.New("failed to convert yaml to json").Base(err) + return nil, newError("failed to convert yaml to json").Base(err) } return DecodeJSONConfig(bytes.NewReader(jsonFile)) @@ -142,7 +142,7 @@ func LoadYAMLConfig(reader io.Reader) (*core.Config, error) { pbConfig, err := yamlConfig.Build() if err != nil { - return nil, errors.New("failed to parse yaml config").Base(err) + return nil, newError("failed to parse yaml config").Base(err) } return pbConfig, nil diff --git a/infra/conf/serial/serial.go b/infra/conf/serial/serial.go index 86baba80..3e6711ff 100644 --- a/infra/conf/serial/serial.go +++ b/infra/conf/serial/serial.go @@ -1 +1,3 @@ package serial + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/infra/conf/shadowsocks.go b/infra/conf/shadowsocks.go index f12040a6..4b94c8e8 100644 --- a/infra/conf/shadowsocks.go +++ b/infra/conf/shadowsocks.go @@ -3,14 +3,13 @@ package conf import ( "strings" + "github.com/golang/protobuf/proto" "github.com/sagernet/sing-shadowsocks/shadowaead_2022" C "github.com/sagernet/sing/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/proxy/shadowsocks" "github.com/xtls/xray-core/proxy/shadowsocks_2022" - "google.golang.org/protobuf/proto" ) func cipherFromString(c string) shadowsocks.CipherType { @@ -65,11 +64,11 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) { IvCheck: v.IVCheck, } if account.Password == "" { - return nil, errors.New("Shadowsocks password is not specified.") + return nil, newError("Shadowsocks password is not specified.") } if account.CipherType < shadowsocks.CipherType_AES_128_GCM || account.CipherType > shadowsocks.CipherType_XCHACHA20_POLY1305 { - return nil, errors.New("unsupported cipher method: ", user.Cipher) + return nil, newError("unsupported cipher method: ", user.Cipher) } config.Users = append(config.Users, &protocol.User{ Email: user.Email, @@ -84,10 +83,10 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) { IvCheck: v.IVCheck, } if account.Password == "" { - return nil, errors.New("Shadowsocks password is not specified.") + return nil, newError("Shadowsocks password is not specified.") } if account.CipherType == shadowsocks.CipherType_UNKNOWN { - return nil, errors.New("unknown cipher method: ", v.Cipher) + return nil, newError("unknown cipher method: ", v.Cipher) } config.Users = append(config.Users, &protocol.User{ Email: v.Email, @@ -110,10 +109,10 @@ func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) { } if v.Cipher == "" { - return nil, errors.New("shadowsocks 2022 (multi-user): missing server method") + return nil, newError("shadowsocks 2022 (multi-user): missing server method") } if !strings.Contains(v.Cipher, "aes") { - return nil, errors.New("shadowsocks 2022 (multi-user): only blake3-aes-*-gcm methods are supported") + return nil, newError("shadowsocks 2022 (multi-user): only blake3-aes-*-gcm methods are supported") } if v.Users[0].Address == nil { @@ -124,15 +123,11 @@ func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) { for _, user := range v.Users { if user.Cipher != "" { - return nil, errors.New("shadowsocks 2022 (multi-user): users must have empty method") + return nil, newError("shadowsocks 2022 (multi-user): users must have empty method") } - account := &shadowsocks_2022.Account{ - Key: user.Password, - } - config.Users = append(config.Users, &protocol.User{ - Email: user.Email, - Level: uint32(user.Level), - Account: serial.ToTypedMessage(account), + config.Users = append(config.Users, &shadowsocks_2022.User{ + Key: user.Password, + Email: user.Email, }) } return config, nil @@ -144,10 +139,10 @@ func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) { config.Network = v.NetworkList.Build() for _, user := range v.Users { if user.Cipher != "" { - return nil, errors.New("shadowsocks 2022 (relay): users must have empty method") + return nil, newError("shadowsocks 2022 (relay): users must have empty method") } if user.Address == nil { - return nil, errors.New("shadowsocks 2022 (relay): all users must have relay address") + return nil, newError("shadowsocks 2022 (relay): all users must have relay address") } config.Destinations = append(config.Destinations, &shadowsocks_2022.RelayDestination{ Key: user.Password, @@ -160,15 +155,14 @@ func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) { } type ShadowsocksServerTarget struct { - Address *Address `json:"address"` - Port uint16 `json:"port"` - Cipher string `json:"method"` - Password string `json:"password"` - Email string `json:"email"` - Level byte `json:"level"` - IVCheck bool `json:"ivCheck"` - UoT bool `json:"uot"` - UoTVersion int `json:"uotVersion"` + Address *Address `json:"address"` + Port uint16 `json:"port"` + Cipher string `json:"method"` + Password string `json:"password"` + Email string `json:"email"` + Level byte `json:"level"` + IVCheck bool `json:"ivCheck"` + UoT bool `json:"uot"` } type ShadowsocksClientConfig struct { @@ -177,20 +171,20 @@ type ShadowsocksClientConfig struct { func (v *ShadowsocksClientConfig) Build() (proto.Message, error) { if len(v.Servers) == 0 { - return nil, errors.New("0 Shadowsocks server configured.") + return nil, newError("0 Shadowsocks server configured.") } if len(v.Servers) == 1 { server := v.Servers[0] if C.Contains(shadowaead_2022.List, server.Cipher) { if server.Address == nil { - return nil, errors.New("Shadowsocks server address is not set.") + return nil, newError("Shadowsocks server address is not set.") } if server.Port == 0 { - return nil, errors.New("Invalid Shadowsocks port.") + return nil, newError("Invalid Shadowsocks port.") } if server.Password == "" { - return nil, errors.New("Shadowsocks password is not specified.") + return nil, newError("Shadowsocks password is not specified.") } config := new(shadowsocks_2022.ClientConfig) @@ -199,7 +193,6 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) { config.Method = server.Cipher config.Key = server.Password config.UdpOverTcp = server.UoT - config.UdpOverTcpVersion = uint32(server.UoTVersion) return config, nil } } @@ -208,23 +201,23 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) { serverSpecs := make([]*protocol.ServerEndpoint, len(v.Servers)) for idx, server := range v.Servers { if C.Contains(shadowaead_2022.List, server.Cipher) { - return nil, errors.New("Shadowsocks 2022 accept no multi servers") + return nil, newError("Shadowsocks 2022 accept no multi servers") } if server.Address == nil { - return nil, errors.New("Shadowsocks server address is not set.") + return nil, newError("Shadowsocks server address is not set.") } if server.Port == 0 { - return nil, errors.New("Invalid Shadowsocks port.") + return nil, newError("Invalid Shadowsocks port.") } if server.Password == "" { - return nil, errors.New("Shadowsocks password is not specified.") + return nil, newError("Shadowsocks password is not specified.") } account := &shadowsocks.Account{ Password: server.Password, } account.CipherType = cipherFromString(server.Cipher) if account.CipherType == shadowsocks.CipherType_UNKNOWN { - return nil, errors.New("unknown cipher method: ", server.Cipher) + return nil, newError("unknown cipher method: ", server.Cipher) } account.IvCheck = server.IVCheck diff --git a/infra/conf/socks.go b/infra/conf/socks.go index 4e14ffdc..490c24bd 100644 --- a/infra/conf/socks.go +++ b/infra/conf/socks.go @@ -2,12 +2,12 @@ package conf import ( "encoding/json" + "strings" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/proxy/socks" - "google.golang.org/protobuf/proto" ) type SocksAccount struct { @@ -32,6 +32,7 @@ type SocksServerConfig struct { Accounts []*SocksAccount `json:"accounts"` UDP bool `json:"udp"` Host *Address `json:"ip"` + Timeout uint32 `json:"timeout"` UserLevel uint32 `json:"userLevel"` } @@ -43,7 +44,7 @@ func (v *SocksServerConfig) Build() (proto.Message, error) { case AuthMethodUserPass: config.AuthType = socks.AuthType_PASSWORD default: - // errors.New("unknown socks auth method: ", v.AuthMethod, ". Default to noauth.").AtWarning().WriteToLog() + // newError("unknown socks auth method: ", v.AuthMethod, ". Default to noauth.").AtWarning().WriteToLog() config.AuthType = socks.AuthType_NO_AUTH } @@ -59,6 +60,7 @@ func (v *SocksServerConfig) Build() (proto.Message, error) { config.Address = v.Host.Build() } + config.Timeout = v.Timeout config.UserLevel = v.UserLevel return config, nil } @@ -71,11 +73,22 @@ type SocksRemoteConfig struct { type SocksClientConfig struct { Servers []*SocksRemoteConfig `json:"servers"` + Version string `json:"version"` } func (v *SocksClientConfig) Build() (proto.Message, error) { config := new(socks.ClientConfig) config.Server = make([]*protocol.ServerEndpoint, len(v.Servers)) + switch strings.ToLower(v.Version) { + case "4": + config.Version = socks.Version_SOCKS4 + case "4a": + config.Version = socks.Version_SOCKS4A + case "", "5": + config.Version = socks.Version_SOCKS5 + default: + return nil, newError("failed to parse socks server version: ", v.Version).AtError() + } for idx, serverConfig := range v.Servers { server := &protocol.ServerEndpoint{ Address: serverConfig.Address.Build(), @@ -84,11 +97,14 @@ func (v *SocksClientConfig) Build() (proto.Message, error) { for _, rawUser := range serverConfig.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { - return nil, errors.New("failed to parse Socks user").Base(err).AtError() + return nil, newError("failed to parse Socks user").Base(err).AtError() } account := new(SocksAccount) if err := json.Unmarshal(rawUser, account); err != nil { - return nil, errors.New("failed to parse socks account").Base(err).AtError() + return nil, newError("failed to parse socks account").Base(err).AtError() + } + if config.Version != socks.Version_SOCKS5 && account.Password != "" { + return nil, newError("password is only supported in socks5").AtError() } user.Account = serial.ToTypedMessage(account.Build()) server.User = append(server.User, user) diff --git a/infra/conf/socks_test.go b/infra/conf/socks_test.go index bd7b570a..ff1833b8 100644 --- a/infra/conf/socks_test.go +++ b/infra/conf/socks_test.go @@ -27,6 +27,7 @@ func TestSocksInboundConfig(t *testing.T) { ], "udp": false, "ip": "127.0.0.1", + "timeout": 5, "userLevel": 1 }`, Parser: loadJSON(creator), @@ -41,6 +42,7 @@ func TestSocksInboundConfig(t *testing.T) { Ip: []byte{127, 0, 0, 1}, }, }, + Timeout: 5, UserLevel: 1, }, }, diff --git a/infra/conf/transport.go b/infra/conf/transport.go new file mode 100644 index 00000000..fa8a3886 --- /dev/null +++ b/infra/conf/transport.go @@ -0,0 +1,105 @@ +package conf + +import ( + "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/transport/global" + "github.com/xtls/xray-core/transport/internet" +) + +type TransportConfig struct { + TCPConfig *TCPConfig `json:"tcpSettings"` + KCPConfig *KCPConfig `json:"kcpSettings"` + WSConfig *WebSocketConfig `json:"wsSettings"` + HTTPConfig *HTTPConfig `json:"httpSettings"` + DSConfig *DomainSocketConfig `json:"dsSettings"` + QUICConfig *QUICConfig `json:"quicSettings"` + GRPCConfig *GRPCConfig `json:"grpcSettings"` + GUNConfig *GRPCConfig `json:"gunSettings"` +} + +// Build implements Buildable. +func (c *TransportConfig) Build() (*global.Config, error) { + config := new(global.Config) + + if c.TCPConfig != nil { + ts, err := c.TCPConfig.Build() + if err != nil { + return nil, newError("failed to build TCP config").Base(err).AtError() + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "tcp", + Settings: serial.ToTypedMessage(ts), + }) + } + + if c.KCPConfig != nil { + ts, err := c.KCPConfig.Build() + if err != nil { + return nil, newError("failed to build mKCP config").Base(err).AtError() + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "mkcp", + Settings: serial.ToTypedMessage(ts), + }) + } + + if c.WSConfig != nil { + ts, err := c.WSConfig.Build() + if err != nil { + return nil, newError("failed to build WebSocket config").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "websocket", + Settings: serial.ToTypedMessage(ts), + }) + } + + if c.HTTPConfig != nil { + ts, err := c.HTTPConfig.Build() + if err != nil { + return nil, newError("Failed to build HTTP config.").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "http", + Settings: serial.ToTypedMessage(ts), + }) + } + + if c.DSConfig != nil { + ds, err := c.DSConfig.Build() + if err != nil { + return nil, newError("Failed to build DomainSocket config.").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "domainsocket", + Settings: serial.ToTypedMessage(ds), + }) + } + + if c.QUICConfig != nil { + qs, err := c.QUICConfig.Build() + if err != nil { + return nil, newError("Failed to build QUIC config.").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "quic", + Settings: serial.ToTypedMessage(qs), + }) + } + + if c.GRPCConfig == nil { + c.GRPCConfig = c.GUNConfig + } + if c.GRPCConfig != nil { + gs, err := c.GRPCConfig.Build() + if err != nil { + return nil, newError("Failed to build gRPC config.").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "grpc", + Settings: serial.ToTypedMessage(gs), + }) + } + + return config, nil +} diff --git a/infra/conf/transport_authenticators.go b/infra/conf/transport_authenticators.go index 4004b26b..46be8588 100644 --- a/infra/conf/transport_authenticators.go +++ b/infra/conf/transport_authenticators.go @@ -3,7 +3,7 @@ package conf import ( "sort" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/transport/internet/headers/dns" "github.com/xtls/xray-core/transport/internet/headers/http" "github.com/xtls/xray-core/transport/internet/headers/noop" @@ -12,7 +12,6 @@ import ( "github.com/xtls/xray-core/transport/internet/headers/utp" "github.com/xtls/xray-core/transport/internet/headers/wechat" "github.com/xtls/xray-core/transport/internet/headers/wireguard" - "google.golang.org/protobuf/proto" ) type NoOpAuthenticator struct{} @@ -134,7 +133,7 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) { for _, key := range headerNames { value := v.Headers[key] if value == nil { - return nil, errors.New("empty HTTP header value: " + key).AtError() + return nil, newError("empty HTTP header value: " + key).AtError() } config.Header = append(config.Header, &http.Header{ Name: key, @@ -202,7 +201,7 @@ func (v *AuthenticatorResponse) Build() (*http.ResponseConfig, error) { for _, key := range headerNames { value := v.Headers[key] if value == nil { - return nil, errors.New("empty HTTP header value: " + key).AtError() + return nil, newError("empty HTTP header value: " + key).AtError() } config.Header = append(config.Header, &http.Header{ Name: key, diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index d762be32..63c8fbed 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -11,19 +11,21 @@ import ( "strings" "syscall" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "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/serial" "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/httpupgrade" + "github.com/xtls/xray-core/transport/internet/domainsocket" + httpheader "github.com/xtls/xray-core/transport/internet/headers/http" + "github.com/xtls/xray-core/transport/internet/http" "github.com/xtls/xray-core/transport/internet/kcp" + "github.com/xtls/xray-core/transport/internet/quic" "github.com/xtls/xray-core/transport/internet/reality" - "github.com/xtls/xray-core/transport/internet/splithttp" "github.com/xtls/xray-core/transport/internet/tcp" "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/websocket" - "google.golang.org/protobuf/proto" ) var ( @@ -62,14 +64,14 @@ func (c *KCPConfig) Build() (proto.Message, error) { if c.Mtu != nil { mtu := *c.Mtu if mtu < 576 || mtu > 1460 { - return nil, errors.New("invalid mKCP MTU size: ", mtu).AtError() + return nil, newError("invalid mKCP MTU size: ", mtu).AtError() } config.Mtu = &kcp.MTU{Value: mtu} } if c.Tti != nil { tti := *c.Tti if tti < 10 || tti > 100 { - return nil, errors.New("invalid mKCP TTI: ", tti).AtError() + return nil, newError("invalid mKCP TTI: ", tti).AtError() } config.Tti = &kcp.TTI{Value: tti} } @@ -101,11 +103,11 @@ func (c *KCPConfig) Build() (proto.Message, error) { if len(c.HeaderConfig) > 0 { headerConfig, _, err := kcpHeaderLoader.Load(c.HeaderConfig) if err != nil { - return nil, errors.New("invalid mKCP header config.").Base(err).AtError() + return nil, newError("invalid mKCP header config.").Base(err).AtError() } ts, err := headerConfig.(Buildable).Build() if err != nil { - return nil, errors.New("invalid mKCP header config").Base(err).AtError() + return nil, newError("invalid mKCP header config").Base(err).AtError() } config.HeaderConfig = serial.ToTypedMessage(ts) } @@ -128,11 +130,11 @@ func (c *TCPConfig) Build() (proto.Message, error) { if len(c.HeaderConfig) > 0 { headerConfig, _, err := tcpHeaderLoader.Load(c.HeaderConfig) if err != nil { - return nil, errors.New("invalid TCP header config").Base(err).AtError() + return nil, newError("invalid TCP header config").Base(err).AtError() } ts, err := headerConfig.(Buildable).Build() if err != nil { - return nil, errors.New("invalid TCP header config").Base(err).AtError() + return nil, newError("invalid TCP header config").Base(err).AtError() } config.HeaderSettings = serial.ToTypedMessage(ts) } @@ -143,16 +145,21 @@ func (c *TCPConfig) Build() (proto.Message, error) { } type WebSocketConfig struct { - Host string `json:"host"` Path string `json:"path"` Headers map[string]string `json:"headers"` AcceptProxyProtocol bool `json:"acceptProxyProtocol"` - HeartbeatPeriod uint32 `json:"heartbeatPeriod"` } // Build implements Buildable. func (c *WebSocketConfig) Build() (proto.Message, error) { path := c.Path + header := make([]*websocket.Header, 0, 32) + for key, value := range c.Headers { + header = append(header, &websocket.Header{ + Key: key, + Value: value, + }) + } var ed uint32 if u, err := url.Parse(path); err == nil { if q := u.Query(); q.Get("ed") != "" { @@ -163,183 +170,126 @@ func (c *WebSocketConfig) Build() (proto.Message, error) { path = u.String() } } - // Priority (client): host > serverName > address - for k, v := range c.Headers { - if strings.ToLower(k) == "host" { - errors.PrintDeprecatedFeatureWarning(`"host" in "headers"`, `independent "host"`) - if c.Host == "" { - c.Host = v - } - delete(c.Headers, k) - } - } config := &websocket.Config{ - Path: path, - Host: c.Host, - Header: c.Headers, - AcceptProxyProtocol: c.AcceptProxyProtocol, - Ed: ed, - HeartbeatPeriod: c.HeartbeatPeriod, + Path: path, + Header: header, + Ed: ed, + } + if c.AcceptProxyProtocol { + config.AcceptProxyProtocol = c.AcceptProxyProtocol } return config, nil } -type HttpUpgradeConfig struct { - Host string `json:"host"` - Path string `json:"path"` - Headers map[string]string `json:"headers"` - AcceptProxyProtocol bool `json:"acceptProxyProtocol"` +type HTTPConfig struct { + Host *StringList `json:"host"` + Path string `json:"path"` + ReadIdleTimeout int32 `json:"read_idle_timeout"` + HealthCheckTimeout int32 `json:"health_check_timeout"` + Method string `json:"method"` + Headers map[string]*StringList `json:"headers"` } // Build implements Buildable. -func (c *HttpUpgradeConfig) Build() (proto.Message, error) { - path := c.Path - var ed uint32 - if u, err := url.Parse(path); err == nil { - if q := u.Query(); q.Get("ed") != "" { - Ed, _ := strconv.Atoi(q.Get("ed")) - ed = uint32(Ed) - q.Del("ed") - u.RawQuery = q.Encode() - path = u.String() - } +func (c *HTTPConfig) Build() (proto.Message, error) { + if c.ReadIdleTimeout <= 0 { + c.ReadIdleTimeout = 0 } - // Priority (client): host > serverName > address - for k := range c.Headers { - if strings.ToLower(k) == "host" { - return nil, errors.New(`"headers" can't contain "host"`) - } + if c.HealthCheckTimeout <= 0 { + c.HealthCheckTimeout = 0 } - config := &httpupgrade.Config{ - Path: path, - Host: c.Host, - Header: c.Headers, - AcceptProxyProtocol: c.AcceptProxyProtocol, - Ed: ed, + config := &http.Config{ + Path: c.Path, + IdleTimeout: c.ReadIdleTimeout, + HealthCheckTimeout: c.HealthCheckTimeout, + } + if c.Host != nil { + config.Host = []string(*c.Host) + } + if c.Method != "" { + config.Method = c.Method + } + if len(c.Headers) > 0 { + config.Header = make([]*httpheader.Header, 0, len(c.Headers)) + headerNames := sortMapKeys(c.Headers) + for _, key := range headerNames { + value := c.Headers[key] + if value == nil { + return nil, newError("empty HTTP header value: " + key).AtError() + } + config.Header = append(config.Header, &httpheader.Header{ + Name: key, + Value: append([]string(nil), (*value)...), + }) + } } return config, nil } -type SplitHTTPConfig struct { - Host string `json:"host"` - Path string `json:"path"` - Mode string `json:"mode"` - Headers map[string]string `json:"headers"` - XPaddingBytes Int32Range `json:"xPaddingBytes"` - NoGRPCHeader bool `json:"noGRPCHeader"` - NoSSEHeader bool `json:"noSSEHeader"` - ScMaxEachPostBytes Int32Range `json:"scMaxEachPostBytes"` - ScMinPostsIntervalMs Int32Range `json:"scMinPostsIntervalMs"` - ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"` - ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"` - Xmux XmuxConfig `json:"xmux"` - DownloadSettings *StreamConfig `json:"downloadSettings"` - Extra json.RawMessage `json:"extra"` -} - -type XmuxConfig struct { - MaxConcurrency Int32Range `json:"maxConcurrency"` - MaxConnections Int32Range `json:"maxConnections"` - CMaxReuseTimes Int32Range `json:"cMaxReuseTimes"` - HMaxRequestTimes Int32Range `json:"hMaxRequestTimes"` - HMaxReusableSecs Int32Range `json:"hMaxReusableSecs"` - HKeepAlivePeriod int64 `json:"hKeepAlivePeriod"` -} - -func newRangeConfig(input Int32Range) *splithttp.RangeConfig { - return &splithttp.RangeConfig{ - From: input.From, - To: input.To, - } +type QUICConfig struct { + Header json.RawMessage `json:"header"` + Security string `json:"security"` + Key string `json:"key"` } // Build implements Buildable. -func (c *SplitHTTPConfig) Build() (proto.Message, error) { - if c.Extra != nil { - var extra SplitHTTPConfig - if err := json.Unmarshal(c.Extra, &extra); err != nil { - return nil, errors.New(`Failed to unmarshal "extra".`).Base(err) - } - extra.Host = c.Host - extra.Path = c.Path - extra.Mode = c.Mode - c = &extra +func (c *QUICConfig) Build() (proto.Message, error) { + config := &quic.Config{ + Key: c.Key, } - switch c.Mode { - case "": - c.Mode = "auto" - case "auto", "packet-up", "stream-up", "stream-one": + if len(c.Header) > 0 { + headerConfig, _, err := kcpHeaderLoader.Load(c.Header) + if err != nil { + return nil, newError("invalid QUIC header config.").Base(err).AtError() + } + ts, err := headerConfig.(Buildable).Build() + if err != nil { + return nil, newError("invalid QUIC header config").Base(err).AtError() + } + config.Header = serial.ToTypedMessage(ts) + } + + var st protocol.SecurityType + switch strings.ToLower(c.Security) { + case "aes-128-gcm": + st = protocol.SecurityType_AES128_GCM + case "chacha20-poly1305": + st = protocol.SecurityType_CHACHA20_POLY1305 default: - return nil, errors.New("unsupported mode: " + c.Mode) + st = protocol.SecurityType_NONE } - // Priority (client): host > serverName > address - for k := range c.Headers { - if strings.ToLower(k) == "host" { - return nil, errors.New(`"headers" can't contain "host"`) - } - } - - if c.XPaddingBytes != (Int32Range{}) && (c.XPaddingBytes.From <= 0 || c.XPaddingBytes.To <= 0) { - return nil, errors.New("xPaddingBytes cannot be disabled") - } - - if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 { - return nil, errors.New("maxConnections cannot be specified together with maxConcurrency") - } - if c.Xmux == (XmuxConfig{}) { - c.Xmux.MaxConcurrency.From = 16 - c.Xmux.MaxConcurrency.To = 32 - c.Xmux.HMaxRequestTimes.From = 600 - c.Xmux.HMaxRequestTimes.To = 900 - c.Xmux.HMaxReusableSecs.From = 1800 - c.Xmux.HMaxReusableSecs.To = 3000 - } - - config := &splithttp.Config{ - Host: c.Host, - Path: c.Path, - Mode: c.Mode, - Headers: c.Headers, - XPaddingBytes: newRangeConfig(c.XPaddingBytes), - NoGRPCHeader: c.NoGRPCHeader, - NoSSEHeader: c.NoSSEHeader, - ScMaxEachPostBytes: newRangeConfig(c.ScMaxEachPostBytes), - ScMinPostsIntervalMs: newRangeConfig(c.ScMinPostsIntervalMs), - ScMaxBufferedPosts: c.ScMaxBufferedPosts, - ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs), - Xmux: &splithttp.XmuxConfig{ - MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency), - MaxConnections: newRangeConfig(c.Xmux.MaxConnections), - CMaxReuseTimes: newRangeConfig(c.Xmux.CMaxReuseTimes), - HMaxRequestTimes: newRangeConfig(c.Xmux.HMaxRequestTimes), - HMaxReusableSecs: newRangeConfig(c.Xmux.HMaxReusableSecs), - HKeepAlivePeriod: c.Xmux.HKeepAlivePeriod, - }, - } - - if c.DownloadSettings != nil { - if c.Mode == "stream-one" { - return nil, errors.New(`Can not use "downloadSettings" in "stream-one" mode.`) - } - var err error - if config.DownloadSettings, err = c.DownloadSettings.Build(); err != nil { - return nil, errors.New(`Failed to build "downloadSettings".`).Base(err) - } + config.Security = &protocol.SecurityConfig{ + Type: st, } return config, nil } +type DomainSocketConfig struct { + Path string `json:"path"` + Abstract bool `json:"abstract"` + Padding bool `json:"padding"` +} + +// Build implements Buildable. +func (c *DomainSocketConfig) Build() (proto.Message, error) { + return &domainsocket.Config{ + Path: c.Path, + Abstract: c.Abstract, + Padding: c.Padding, + }, nil +} + func readFileOrString(f string, s []string) ([]byte, error) { if len(f) > 0 { - return filesystem.ReadCert(f) + return filesystem.ReadFile(f) } if len(s) > 0 { return []byte(strings.Join(s, "\n")), nil } - return nil, errors.New("both file and bytes are empty.") + return nil, newError("both file and bytes are empty.") } type TLSCertConfig struct { @@ -350,7 +300,6 @@ type TLSCertConfig struct { Usage string `json:"usage"` OcspStapling uint64 `json:"ocspStapling"` OneTimeLoading bool `json:"oneTimeLoading"` - BuildChain bool `json:"buildChain"` } // Build implements Buildable. @@ -359,7 +308,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) { cert, err := readFileOrString(c.CertFile, c.CertStr) if err != nil { - return nil, errors.New("failed to parse certificate").Base(err) + return nil, newError("failed to parse certificate").Base(err) } certificate.Certificate = cert certificate.CertificatePath = c.CertFile @@ -367,7 +316,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) { if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 { key, err := readFileOrString(c.KeyFile, c.KeyStr) if err != nil { - return nil, errors.New("failed to parse key").Base(err) + return nil, newError("failed to parse key").Base(err) } certificate.Key = key certificate.KeyPath = c.KeyFile @@ -389,7 +338,6 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) { certificate.OneTimeLoading = c.OneTimeLoading } certificate.OcspStapling = c.OcspStapling - certificate.BuildChain = c.BuildChain return certificate, nil } @@ -404,14 +352,11 @@ type TLSConfig struct { MinVersion string `json:"minVersion"` MaxVersion string `json:"maxVersion"` CipherSuites string `json:"cipherSuites"` + PreferServerCipherSuites bool `json:"preferServerCipherSuites"` Fingerprint string `json:"fingerprint"` RejectUnknownSNI bool `json:"rejectUnknownSni"` PinnedPeerCertificateChainSha256 *[]string `json:"pinnedPeerCertificateChainSha256"` PinnedPeerCertificatePublicKeySha256 *[]string `json:"pinnedPeerCertificatePublicKeySha256"` - CurvePreferences *StringList `json:"curvePreferences"` - MasterKeyLog string `json:"masterKeyLog"` - ServerNameToVerify string `json:"serverNameToVerify"` - VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"` } // Build implements Buildable. @@ -433,24 +378,15 @@ func (c *TLSConfig) Build() (proto.Message, error) { if c.ALPN != nil && len(*c.ALPN) > 0 { config.NextProtocol = []string(*c.ALPN) } - if len(config.NextProtocol) > 1 { - for _, p := range config.NextProtocol { - if tcp.IsFromMitm(p) { - return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`) - } - } - } - if c.CurvePreferences != nil && len(*c.CurvePreferences) > 0 { - config.CurvePreferences = []string(*c.CurvePreferences) - } config.EnableSessionResumption = c.EnableSessionResumption config.DisableSystemRoot = c.DisableSystemRoot config.MinVersion = c.MinVersion config.MaxVersion = c.MaxVersion config.CipherSuites = c.CipherSuites + config.PreferServerCipherSuites = c.PreferServerCipherSuites config.Fingerprint = strings.ToLower(c.Fingerprint) - if config.Fingerprint != "unsafe" && tls.GetFingerprint(config.Fingerprint) == nil { - return nil, errors.New(`unknown "fingerprint": `, config.Fingerprint) + if config.Fingerprint != "" && tls.GetFingerprint(config.Fingerprint) == nil { + return nil, newError(`unknown fingerprint: `, config.Fingerprint) } config.RejectUnknownSni = c.RejectUnknownSNI @@ -476,26 +412,11 @@ func (c *TLSConfig) Build() (proto.Message, error) { } } - config.MasterKeyLog = c.MasterKeyLog - - if c.ServerNameToVerify != "" { - return nil, errors.PrintRemovedFeatureError(`"serverNameToVerify"`, `"verifyPeerCertInNames"`) - } - config.VerifyPeerCertInNames = c.VerifyPeerCertInNames - return config, nil } -type LimitFallback struct { - AfterBytes uint64 - BytesPerSec uint64 - BurstBytesPerSec uint64 -} - type REALITYConfig struct { - MasterKeyLog string `json:"masterKeyLog"` Show bool `json:"show"` - Target json.RawMessage `json:"target"` Dest json.RawMessage `json:"dest"` Type string `json:"type"` Xver uint64 `json:"xver"` @@ -506,12 +427,8 @@ type REALITYConfig struct { MaxTimeDiff uint64 `json:"maxTimeDiff"` ShortIds []string `json:"shortIds"` - LimitFallbackUpload LimitFallback `json:"limitFallbackUpload"` - LimitFallbackDownload LimitFallback `json:"limitFallbackDownload"` - Fingerprint string `json:"fingerprint"` ServerName string `json:"serverName"` - Password string `json:"password"` PublicKey string `json:"publicKey"` ShortId string `json:"shortId"` SpiderX string `json:"spiderX"` @@ -519,12 +436,8 @@ type REALITYConfig struct { func (c *REALITYConfig) Build() (proto.Message, error) { config := new(reality.Config) - config.MasterKeyLog = c.MasterKeyLog config.Show = c.Show var err error - if c.Target != nil { - c.Dest = c.Target - } if c.Dest != nil { var i uint16 var s string @@ -552,29 +465,29 @@ func (c *REALITYConfig) Build() (proto.Message, error) { } } if c.Type == "" { - return nil, errors.New(`please fill in a valid value for "target"`) + return nil, newError(`please fill in a valid value for "dest"`) } if c.Xver > 2 { - return nil, errors.New(`invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) + return nil, newError(`invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) } if len(c.ServerNames) == 0 { - return nil, errors.New(`empty "serverNames"`) + return nil, newError(`empty "serverNames"`) } if c.PrivateKey == "" { - return nil, errors.New(`empty "privateKey"`) + return nil, newError(`empty "privateKey"`) } if config.PrivateKey, err = base64.RawURLEncoding.DecodeString(c.PrivateKey); err != nil || len(config.PrivateKey) != 32 { - return nil, errors.New(`invalid "privateKey": `, c.PrivateKey) + return nil, newError(`invalid "privateKey": `, c.PrivateKey) } if c.MinClientVer != "" { config.MinClientVer = make([]byte, 3) var u uint64 for i, s := range strings.Split(c.MinClientVer, ".") { if i == 3 { - return nil, errors.New(`invalid "minClientVer": `, c.MinClientVer) + return nil, newError(`invalid "minClientVer": `, c.MinClientVer) } if u, err = strconv.ParseUint(s, 10, 8); err != nil { - return nil, errors.New(`"minClientVer[`, i, `]" should be less than 256`) + return nil, newError(`"minClientVer[`, i, `]" should be lesser than 256`) } else { config.MinClientVer[i] = byte(u) } @@ -585,23 +498,23 @@ func (c *REALITYConfig) Build() (proto.Message, error) { var u uint64 for i, s := range strings.Split(c.MaxClientVer, ".") { if i == 3 { - return nil, errors.New(`invalid "maxClientVer": `, c.MaxClientVer) + return nil, newError(`invalid "maxClientVer": `, c.MaxClientVer) } if u, err = strconv.ParseUint(s, 10, 8); err != nil { - return nil, errors.New(`"maxClientVer[`, i, `]" should be less than 256`) + return nil, newError(`"maxClientVer[`, i, `]" should be lesser than 256`) } else { config.MaxClientVer[i] = byte(u) } } } if len(c.ShortIds) == 0 { - return nil, errors.New(`empty "shortIds"`) + return nil, newError(`empty "shortIds"`) } config.ShortIds = make([][]byte, len(c.ShortIds)) for i, s := range c.ShortIds { config.ShortIds[i] = make([]byte, 8) if _, err = hex.Decode(config.ShortIds[i], []byte(s)); err != nil { - return nil, errors.New(`invalid "shortIds[`, i, `]": `, s) + return nil, newError(`invalid "shortIds[`, i, `]": `, s) } } config.Dest = s @@ -609,47 +522,37 @@ func (c *REALITYConfig) Build() (proto.Message, error) { config.Xver = c.Xver config.ServerNames = c.ServerNames config.MaxTimeDiff = c.MaxTimeDiff - - config.LimitFallbackUpload = new(reality.LimitFallback) - config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes - config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec - config.LimitFallbackUpload.BurstBytesPerSec = c.LimitFallbackUpload.BurstBytesPerSec - config.LimitFallbackDownload = new(reality.LimitFallback) - config.LimitFallbackDownload.AfterBytes = c.LimitFallbackDownload.AfterBytes - config.LimitFallbackDownload.BytesPerSec = c.LimitFallbackDownload.BytesPerSec - config.LimitFallbackDownload.BurstBytesPerSec = c.LimitFallbackDownload.BurstBytesPerSec } else { - config.Fingerprint = strings.ToLower(c.Fingerprint) - if config.Fingerprint == "unsafe" || config.Fingerprint == "hellogolang" { - return nil, errors.New(`invalid "fingerprint": `, config.Fingerprint) + if c.Fingerprint == "" { + return nil, newError(`empty "fingerprint"`) } - if tls.GetFingerprint(config.Fingerprint) == nil { - return nil, errors.New(`unknown "fingerprint": `, config.Fingerprint) + if config.Fingerprint = strings.ToLower(c.Fingerprint); tls.GetFingerprint(config.Fingerprint) == nil { + return nil, newError(`unknown "fingerprint": `, config.Fingerprint) + } + if config.Fingerprint == "hellogolang" { + return nil, newError(`invalid "fingerprint": `, config.Fingerprint) } if len(c.ServerNames) != 0 { - return nil, errors.New(`non-empty "serverNames", please use "serverName" instead`) - } - if c.Password != "" { - c.PublicKey = c.Password + return nil, newError(`non-empty "serverNames", please use "serverName" instead`) } if c.PublicKey == "" { - return nil, errors.New(`empty "password"`) + return nil, newError(`empty "publicKey"`) } if config.PublicKey, err = base64.RawURLEncoding.DecodeString(c.PublicKey); err != nil || len(config.PublicKey) != 32 { - return nil, errors.New(`invalid "password": `, c.PublicKey) + return nil, newError(`invalid "publicKey": `, c.PublicKey) } if len(c.ShortIds) != 0 { - return nil, errors.New(`non-empty "shortIds", please use "shortId" instead`) + return nil, newError(`non-empty "shortIds", please use "shortId" instead`) } config.ShortId = make([]byte, 8) if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil { - return nil, errors.New(`invalid "shortId": `, c.ShortId) + return nil, newError(`invalid "shortId": `, c.ShortId) } if c.SpiderX == "" { c.SpiderX = "/" } if c.SpiderX[0] != '/' { - return nil, errors.New(`invalid "spiderX": `, c.SpiderX) + return nil, newError(`invalid "spiderX": `, c.SpiderX) } config.SpiderY = make([]int64, 10) u, _ := url.Parse(c.SpiderX) @@ -684,83 +587,38 @@ type TransportProtocol string // Build implements Buildable. func (p TransportProtocol) Build() (string, error) { switch strings.ToLower(string(p)) { - case "raw", "tcp": + case "tcp": return "tcp", nil - case "xhttp", "splithttp": - return "splithttp", nil case "kcp", "mkcp": return "mkcp", nil - case "grpc": - errors.PrintDeprecatedFeatureWarning("gRPC transport (with unnecessary costs, etc.)", "XHTTP stream-up H2") - return "grpc", nil case "ws", "websocket": - errors.PrintDeprecatedFeatureWarning("WebSocket transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3") return "websocket", nil - case "httpupgrade": - errors.PrintDeprecatedFeatureWarning("HTTPUpgrade transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3") - return "httpupgrade", nil - case "h2", "h3", "http": - return "", errors.PrintRemovedFeatureError("HTTP transport (without header padding, etc.)", "XHTTP stream-one H2 & H3") + case "h2", "http": + return "http", nil + case "ds", "domainsocket": + return "domainsocket", nil case "quic": - return "", errors.PrintRemovedFeatureError("QUIC transport (without web service, etc.)", "XHTTP stream-one H3") + return "quic", nil + case "grpc", "gun": + return "grpc", nil default: - return "", errors.New("Config: unknown transport protocol: ", p) + return "", newError("Config: unknown transport protocol: ", p) } } -type CustomSockoptConfig struct { - Syetem string `json:"system"` - Network string `json:"network"` - Level string `json:"level"` - Opt string `json:"opt"` - Value string `json:"value"` - Type string `json:"type"` -} - -type HappyEyeballsConfig struct { - PrioritizeIPv6 bool `json:"prioritizeIPv6"` - TryDelayMs uint64 `json:"tryDelayMs"` - Interleave uint32 `json:"interleave"` - MaxConcurrentTry uint32 `json:"maxConcurrentTry"` -} - -func (h *HappyEyeballsConfig) UnmarshalJSON(data []byte) error { - var innerHappyEyeballsConfig = struct { - PrioritizeIPv6 bool `json:"prioritizeIPv6"` - TryDelayMs uint64 `json:"tryDelayMs"` - Interleave uint32 `json:"interleave"` - MaxConcurrentTry uint32 `json:"maxConcurrentTry"` - }{PrioritizeIPv6: false, Interleave: 1, TryDelayMs: 0, MaxConcurrentTry: 4} - if err := json.Unmarshal(data, &innerHappyEyeballsConfig); err != nil { - return err - } - h.PrioritizeIPv6 = innerHappyEyeballsConfig.PrioritizeIPv6 - h.TryDelayMs = innerHappyEyeballsConfig.TryDelayMs - h.Interleave = innerHappyEyeballsConfig.Interleave - h.MaxConcurrentTry = innerHappyEyeballsConfig.MaxConcurrentTry - return nil -} - type SocketConfig struct { - Mark int32 `json:"mark"` - TFO interface{} `json:"tcpFastOpen"` - TProxy string `json:"tproxy"` - AcceptProxyProtocol bool `json:"acceptProxyProtocol"` - DomainStrategy string `json:"domainStrategy"` - DialerProxy string `json:"dialerProxy"` - TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"` - TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"` - TCPCongestion string `json:"tcpCongestion"` - TCPWindowClamp int32 `json:"tcpWindowClamp"` - TCPMaxSeg int32 `json:"tcpMaxSeg"` - Penetrate bool `json:"penetrate"` - TCPUserTimeout int32 `json:"tcpUserTimeout"` - V6only bool `json:"v6only"` - Interface string `json:"interface"` - TcpMptcp bool `json:"tcpMptcp"` - CustomSockopt []*CustomSockoptConfig `json:"customSockopt"` - AddressPortStrategy string `json:"addressPortStrategy"` - HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"` + Mark int32 `json:"mark"` + TFO interface{} `json:"tcpFastOpen"` + TProxy string `json:"tproxy"` + AcceptProxyProtocol bool `json:"acceptProxyProtocol"` + DomainStrategy string `json:"domainStrategy"` + DialerProxy string `json:"dialerProxy"` + TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"` + TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"` + TCPCongestion string `json:"tcpCongestion"` + TCPWindowClamp int32 `json:"tcpWindowClamp"` + V6only bool `json:"v6only"` + Interface string `json:"interface"` } // Build implements Buildable. @@ -777,7 +635,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { case float64: tfo = int32(math.Min(v, math.MaxInt32)) default: - return nil, errors.New("tcpFastOpen: only boolean and integer value is acceptable") + return nil, newError("tcpFastOpen: only boolean and integer value is acceptable") } } var tproxy internet.SocketConfig_TProxyMode @@ -792,72 +650,12 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { dStrategy := internet.DomainStrategy_AS_IS switch strings.ToLower(c.DomainStrategy) { - case "asis", "": - dStrategy = internet.DomainStrategy_AS_IS - case "useip": + case "useip", "use_ip": dStrategy = internet.DomainStrategy_USE_IP - case "useipv4": + case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4": dStrategy = internet.DomainStrategy_USE_IP4 - case "useipv6": + case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6": dStrategy = internet.DomainStrategy_USE_IP6 - case "useipv4v6": - dStrategy = internet.DomainStrategy_USE_IP46 - case "useipv6v4": - dStrategy = internet.DomainStrategy_USE_IP64 - case "forceip": - dStrategy = internet.DomainStrategy_FORCE_IP - case "forceipv4": - dStrategy = internet.DomainStrategy_FORCE_IP4 - case "forceipv6": - dStrategy = internet.DomainStrategy_FORCE_IP6 - case "forceipv4v6": - dStrategy = internet.DomainStrategy_FORCE_IP46 - case "forceipv6v4": - dStrategy = internet.DomainStrategy_FORCE_IP64 - default: - return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy) - } - - var customSockopts []*internet.CustomSockopt - - for _, copt := range c.CustomSockopt { - customSockopt := &internet.CustomSockopt{ - System: copt.Syetem, - Network: copt.Network, - Level: copt.Level, - Opt: copt.Opt, - Value: copt.Value, - Type: copt.Type, - } - customSockopts = append(customSockopts, customSockopt) - } - - addressPortStrategy := internet.AddressPortStrategy_None - switch strings.ToLower(c.AddressPortStrategy) { - case "none", "": - addressPortStrategy = internet.AddressPortStrategy_None - case "srvportonly": - addressPortStrategy = internet.AddressPortStrategy_SrvPortOnly - case "srvaddressonly": - addressPortStrategy = internet.AddressPortStrategy_SrvAddressOnly - case "srvportandaddress": - addressPortStrategy = internet.AddressPortStrategy_SrvPortAndAddress - case "txtportonly": - addressPortStrategy = internet.AddressPortStrategy_TxtPortOnly - case "txtaddressonly": - addressPortStrategy = internet.AddressPortStrategy_TxtAddressOnly - case "txtportandaddress": - addressPortStrategy = internet.AddressPortStrategy_TxtPortAndAddress - default: - return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy) - } - - var happyEyeballs = &internet.HappyEyeballsConfig{Interleave: 1, PrioritizeIpv6: false, TryDelayMs: 0, MaxConcurrentTry: 4} - if c.HappyEyeballsSettings != nil { - happyEyeballs.PrioritizeIpv6 = c.HappyEyeballsSettings.PrioritizeIPv6 - happyEyeballs.Interleave = c.HappyEyeballsSettings.Interleave - happyEyeballs.TryDelayMs = c.HappyEyeballsSettings.TryDelayMs - happyEyeballs.MaxConcurrentTry = c.HappyEyeballsSettings.MaxConcurrentTry } return &internet.SocketConfig{ @@ -870,46 +668,33 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { TcpKeepAliveInterval: c.TCPKeepAliveInterval, TcpKeepAliveIdle: c.TCPKeepAliveIdle, TcpCongestion: c.TCPCongestion, - TcpWindowClamp: c.TCPWindowClamp, - TcpMaxSeg: c.TCPMaxSeg, - Penetrate: c.Penetrate, - TcpUserTimeout: c.TCPUserTimeout, + TcpWindowClamp: c.TCPWindowClamp, V6Only: c.V6only, Interface: c.Interface, - TcpMptcp: c.TcpMptcp, - CustomSockopt: customSockopts, - AddressPortStrategy: addressPortStrategy, - HappyEyeballs: happyEyeballs, }, nil } type StreamConfig struct { - Address *Address `json:"address"` - Port uint16 `json:"port"` - Network *TransportProtocol `json:"network"` - Security string `json:"security"` - TLSSettings *TLSConfig `json:"tlsSettings"` - REALITYSettings *REALITYConfig `json:"realitySettings"` - RAWSettings *TCPConfig `json:"rawSettings"` - TCPSettings *TCPConfig `json:"tcpSettings"` - XHTTPSettings *SplitHTTPConfig `json:"xhttpSettings"` - SplitHTTPSettings *SplitHTTPConfig `json:"splithttpSettings"` - KCPSettings *KCPConfig `json:"kcpSettings"` - GRPCSettings *GRPCConfig `json:"grpcSettings"` - WSSettings *WebSocketConfig `json:"wsSettings"` - HTTPUPGRADESettings *HttpUpgradeConfig `json:"httpupgradeSettings"` - SocketSettings *SocketConfig `json:"sockopt"` + Network *TransportProtocol `json:"network"` + Security string `json:"security"` + TLSSettings *TLSConfig `json:"tlsSettings"` + REALITYSettings *REALITYConfig `json:"realitySettings"` + TCPSettings *TCPConfig `json:"tcpSettings"` + KCPSettings *KCPConfig `json:"kcpSettings"` + WSSettings *WebSocketConfig `json:"wsSettings"` + HTTPSettings *HTTPConfig `json:"httpSettings"` + DSSettings *DomainSocketConfig `json:"dsSettings"` + QUICSettings *QUICConfig `json:"quicSettings"` + SocketSettings *SocketConfig `json:"sockopt"` + GRPCConfig *GRPCConfig `json:"grpcSettings"` + GUNConfig *GRPCConfig `json:"gunSettings"` } // Build implements Buildable. func (c *StreamConfig) Build() (*internet.StreamConfig, error) { config := &internet.StreamConfig{ - Port: uint32(c.Port), ProtocolName: "tcp", } - if c.Address != nil { - config.Address = c.Address.Build() - } if c.Network != nil { protocol, err := c.Network.Build() if err != nil { @@ -926,100 +711,107 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) { } ts, err := tlsSettings.Build() if err != nil { - return nil, errors.New("Failed to build TLS config.").Base(err) + return nil, newError("Failed to build TLS config.").Base(err) } tm := serial.ToTypedMessage(ts) config.SecuritySettings = append(config.SecuritySettings, tm) config.SecurityType = tm.Type case "reality": - if config.ProtocolName != "tcp" && config.ProtocolName != "splithttp" && config.ProtocolName != "grpc" { - return nil, errors.New("REALITY only supports RAW, XHTTP and gRPC for now.") + if config.ProtocolName != "tcp" && config.ProtocolName != "http" && config.ProtocolName != "grpc" && config.ProtocolName != "domainsocket" { + return nil, newError("REALITY only supports TCP, H2, gRPC and DomainSocket for now.") } if c.REALITYSettings == nil { - return nil, errors.New(`REALITY: Empty "realitySettings".`) + return nil, newError(`REALITY: Empty "realitySettings".`) } ts, err := c.REALITYSettings.Build() if err != nil { - return nil, errors.New("Failed to build REALITY config.").Base(err) + return nil, newError("Failed to build REALITY config.").Base(err) } tm := serial.ToTypedMessage(ts) config.SecuritySettings = append(config.SecuritySettings, tm) config.SecurityType = tm.Type case "xtls": - return nil, errors.PrintRemovedFeatureError(`Legacy XTLS`, `xtls-rprx-vision with TLS or REALITY`) + return nil, newError(`Please use VLESS flow "xtls-rprx-vision" with TLS or REALITY.`) default: - return nil, errors.New(`Unknown security "` + c.Security + `".`) - } - if c.RAWSettings != nil { - c.TCPSettings = c.RAWSettings + return nil, newError(`Unknown security "` + c.Security + `".`) } if c.TCPSettings != nil { ts, err := c.TCPSettings.Build() if err != nil { - return nil, errors.New("Failed to build RAW config.").Base(err) + return nil, newError("Failed to build TCP config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "tcp", Settings: serial.ToTypedMessage(ts), }) } - if c.XHTTPSettings != nil { - c.SplitHTTPSettings = c.XHTTPSettings - } - if c.SplitHTTPSettings != nil { - hs, err := c.SplitHTTPSettings.Build() - if err != nil { - return nil, errors.New("Failed to build XHTTP config.").Base(err) - } - config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ - ProtocolName: "splithttp", - Settings: serial.ToTypedMessage(hs), - }) - } if c.KCPSettings != nil { ts, err := c.KCPSettings.Build() if err != nil { - return nil, errors.New("Failed to build mKCP config.").Base(err) + return nil, newError("Failed to build mKCP config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "mkcp", Settings: serial.ToTypedMessage(ts), }) } - if c.GRPCSettings != nil { - gs, err := c.GRPCSettings.Build() - if err != nil { - return nil, errors.New("Failed to build gRPC config.").Base(err) - } - config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ - ProtocolName: "grpc", - Settings: serial.ToTypedMessage(gs), - }) - } if c.WSSettings != nil { ts, err := c.WSSettings.Build() if err != nil { - return nil, errors.New("Failed to build WebSocket config.").Base(err) + return nil, newError("Failed to build WebSocket config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "websocket", Settings: serial.ToTypedMessage(ts), }) } - if c.HTTPUPGRADESettings != nil { - hs, err := c.HTTPUPGRADESettings.Build() + if c.HTTPSettings != nil { + ts, err := c.HTTPSettings.Build() if err != nil { - return nil, errors.New("Failed to build HTTPUpgrade config.").Base(err) + return nil, newError("Failed to build HTTP config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ - ProtocolName: "httpupgrade", - Settings: serial.ToTypedMessage(hs), + ProtocolName: "http", + Settings: serial.ToTypedMessage(ts), + }) + } + if c.DSSettings != nil { + ds, err := c.DSSettings.Build() + if err != nil { + return nil, newError("Failed to build DomainSocket config.").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "domainsocket", + Settings: serial.ToTypedMessage(ds), + }) + } + if c.QUICSettings != nil { + qs, err := c.QUICSettings.Build() + if err != nil { + return nil, newError("Failed to build QUIC config").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "quic", + Settings: serial.ToTypedMessage(qs), + }) + } + if c.GRPCConfig == nil { + c.GRPCConfig = c.GUNConfig + } + if c.GRPCConfig != nil { + gs, err := c.GRPCConfig.Build() + if err != nil { + return nil, newError("Failed to build gRPC config.").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "grpc", + Settings: serial.ToTypedMessage(gs), }) } if c.SocketSettings != nil { ss, err := c.SocketSettings.Build() if err != nil { - return nil, errors.New("Failed to build sockopt.").Base(err) + return nil, newError("Failed to build sockopt").Base(err) } config.SocketSettings = ss } @@ -1036,7 +828,7 @@ type ProxyConfig struct { // Build implements Buildable. func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) { if v.Tag == "" { - return nil, errors.New("Proxy tag is not set.") + return nil, newError("Proxy tag is not set.") } return &internet.ProxyConfig{ Tag: v.Tag, diff --git a/infra/conf/transport_test.go b/infra/conf/transport_test.go index 87e5f920..3afe9729 100644 --- a/infra/conf/transport_test.go +++ b/infra/conf/transport_test.go @@ -4,9 +4,20 @@ import ( "encoding/json" "testing" + "github.com/golang/protobuf/proto" + "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/serial" . "github.com/xtls/xray-core/infra/conf" + "github.com/xtls/xray-core/transport/global" "github.com/xtls/xray-core/transport/internet" - "google.golang.org/protobuf/proto" + "github.com/xtls/xray-core/transport/internet/grpc" + "github.com/xtls/xray-core/transport/internet/headers/http" + "github.com/xtls/xray-core/transport/internet/headers/noop" + "github.com/xtls/xray-core/transport/internet/headers/tls" + "github.com/xtls/xray-core/transport/internet/kcp" + "github.com/xtls/xray-core/transport/internet/quic" + "github.com/xtls/xray-core/transport/internet/tcp" + "github.com/xtls/xray-core/transport/internet/websocket" ) func TestSocketConfig(t *testing.T) { @@ -26,7 +37,6 @@ func TestSocketConfig(t *testing.T) { Tfo: 256, DomainStrategy: internet.DomainStrategy_USE_IP, DialerProxy: "tag", - HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4}, } runMultiTestCase(t, []TestCase{ { @@ -46,9 +56,8 @@ func TestSocketConfig(t *testing.T) { // test "tcpFastOpen": false, disabled TFO is expected expectedOutput = &internet.SocketConfig{ - Mark: 0, - Tfo: -1, - HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4}, + Mark: 0, + Tfo: -1, } runMultiTestCase(t, []TestCase{ { @@ -65,9 +74,8 @@ func TestSocketConfig(t *testing.T) { // test "tcpFastOpen": 65535, queue length 65535 is expected expectedOutput = &internet.SocketConfig{ - Mark: 0, - Tfo: 65535, - HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4}, + Mark: 0, + Tfo: 65535, } runMultiTestCase(t, []TestCase{ { @@ -84,9 +92,8 @@ func TestSocketConfig(t *testing.T) { // test "tcpFastOpen": -65535, disable TFO is expected expectedOutput = &internet.SocketConfig{ - Mark: 0, - Tfo: -65535, - HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4}, + Mark: 0, + Tfo: -65535, } runMultiTestCase(t, []TestCase{ { @@ -103,9 +110,8 @@ func TestSocketConfig(t *testing.T) { // test "tcpFastOpen": 0, no operation is expected expectedOutput = &internet.SocketConfig{ - Mark: 0, - Tfo: 0, - HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4}, + Mark: 0, + Tfo: 0, } runMultiTestCase(t, []TestCase{ { @@ -122,9 +128,8 @@ func TestSocketConfig(t *testing.T) { // test omit "tcpFastOpen", no operation is expected expectedOutput = &internet.SocketConfig{ - Mark: 0, - Tfo: 0, - HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4}, + Mark: 0, + Tfo: 0, } runMultiTestCase(t, []TestCase{ { @@ -139,9 +144,8 @@ func TestSocketConfig(t *testing.T) { // test "tcpFastOpen": null, no operation is expected expectedOutput = &internet.SocketConfig{ - Mark: 0, - Tfo: 0, - HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4}, + Mark: 0, + Tfo: 0, } runMultiTestCase(t, []TestCase{ { @@ -156,3 +160,155 @@ func TestSocketConfig(t *testing.T) { t.Fatalf("unexpected parsed TFO value, which should be -1") } } + +func TestTransportConfig(t *testing.T) { + createParser := func() func(string) (proto.Message, error) { + return func(s string) (proto.Message, error) { + config := new(TransportConfig) + if err := json.Unmarshal([]byte(s), config); err != nil { + return nil, err + } + return config.Build() + } + } + + runMultiTestCase(t, []TestCase{ + { + Input: `{ + "tcpSettings": { + "header": { + "type": "http", + "request": { + "version": "1.1", + "method": "GET", + "path": "/b", + "headers": { + "a": "b", + "c": "d" + } + }, + "response": { + "version": "1.0", + "status": "404", + "reason": "Not Found" + } + } + }, + "kcpSettings": { + "mtu": 1200, + "header": { + "type": "none" + } + }, + "wsSettings": { + "path": "/t" + }, + "quicSettings": { + "key": "abcd", + "header": { + "type": "dtls" + } + }, + "grpcSettings": { + "serviceName": "name", + "multiMode": true + } + }`, + Parser: createParser(), + Output: &global.Config{ + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "tcp", + Settings: serial.ToTypedMessage(&tcp.Config{ + HeaderSettings: serial.ToTypedMessage(&http.Config{ + Request: &http.RequestConfig{ + Version: &http.Version{Value: "1.1"}, + Method: &http.Method{Value: "GET"}, + Uri: []string{"/b"}, + Header: []*http.Header{ + {Name: "a", Value: []string{"b"}}, + {Name: "c", Value: []string{"d"}}, + }, + }, + Response: &http.ResponseConfig{ + Version: &http.Version{Value: "1.0"}, + Status: &http.Status{Code: "404", Reason: "Not Found"}, + Header: []*http.Header{ + { + Name: "Content-Type", + Value: []string{"application/octet-stream", "video/mpeg"}, + }, + { + Name: "Transfer-Encoding", + Value: []string{"chunked"}, + }, + { + Name: "Connection", + Value: []string{"keep-alive"}, + }, + { + Name: "Pragma", + Value: []string{"no-cache"}, + }, + { + Name: "Cache-Control", + Value: []string{"private", "no-cache"}, + }, + }, + }, + }), + }), + }, + { + ProtocolName: "mkcp", + Settings: serial.ToTypedMessage(&kcp.Config{ + Mtu: &kcp.MTU{Value: 1200}, + HeaderConfig: serial.ToTypedMessage(&noop.Config{}), + }), + }, + { + ProtocolName: "websocket", + Settings: serial.ToTypedMessage(&websocket.Config{ + Path: "/t", + }), + }, + { + ProtocolName: "quic", + Settings: serial.ToTypedMessage(&quic.Config{ + Key: "abcd", + Security: &protocol.SecurityConfig{ + Type: protocol.SecurityType_NONE, + }, + Header: serial.ToTypedMessage(&tls.PacketConfig{}), + }), + }, + { + ProtocolName: "grpc", + Settings: serial.ToTypedMessage(&grpc.Config{ + ServiceName: "name", + MultiMode: true, + }), + }, + }, + }, + }, + { + Input: `{ + "gunSettings": { + "serviceName": "name" + } + }`, + Parser: createParser(), + Output: &global.Config{ + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "grpc", + Settings: serial.ToTypedMessage(&grpc.Config{ + ServiceName: "name", + }), + }, + }, + }, + }, + }) +} diff --git a/infra/conf/trojan.go b/infra/conf/trojan.go index 62c8f55b..e7d8738b 100644 --- a/infra/conf/trojan.go +++ b/infra/conf/trojan.go @@ -2,18 +2,15 @@ package conf import ( "encoding/json" - "path/filepath" "runtime" "strconv" - "strings" "syscall" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/proxy/trojan" - "google.golang.org/protobuf/proto" ) // TrojanServerTarget is configuration of a single trojan server @@ -33,43 +30,51 @@ type TrojanClientConfig struct { // Build implements Buildable func (c *TrojanClientConfig) Build() (proto.Message, error) { + config := new(trojan.ClientConfig) + if len(c.Servers) == 0 { - return nil, errors.New("0 Trojan server configured.") - } - - config := &trojan.ClientConfig{ - Server: make([]*protocol.ServerEndpoint, len(c.Servers)), + return nil, newError("0 Trojan server configured.") } + serverSpecs := make([]*protocol.ServerEndpoint, len(c.Servers)) for idx, rec := range c.Servers { if rec.Address == nil { - return nil, errors.New("Trojan server address is not set.") + return nil, newError("Trojan server address is not set.") } if rec.Port == 0 { - return nil, errors.New("Invalid Trojan port.") + return nil, newError("Invalid Trojan port.") } if rec.Password == "" { - return nil, errors.New("Trojan password is not specified.") + return nil, newError("Trojan password is not specified.") } - if rec.Flow != "" { - return nil, errors.PrintRemovedFeatureError(`Flow for Trojan`, ``) + account := &trojan.Account{ + Password: rec.Password, + Flow: rec.Flow, } - config.Server[idx] = &protocol.ServerEndpoint{ + switch account.Flow { + case "": + default: + return nil, newError(`Trojan servers: "flow" doesn't support "` + account.Flow + `" in this version`) + } + + trojan := &protocol.ServerEndpoint{ Address: rec.Address.Build(), Port: uint32(rec.Port), User: []*protocol.User{ { - Level: uint32(rec.Level), - Email: rec.Email, - Account: serial.ToTypedMessage(&trojan.Account{ - Password: rec.Password, - }), + Level: uint32(rec.Level), + Email: rec.Email, + Account: serial.ToTypedMessage(account), }, }, } + + serverSpecs[idx] = trojan } + config.Server = serverSpecs + return config, nil } @@ -94,29 +99,36 @@ type TrojanUserConfig struct { // TrojanServerConfig is Inbound configuration type TrojanServerConfig struct { Clients []*TrojanUserConfig `json:"clients"` + Fallback *TrojanInboundFallback `json:"fallback"` Fallbacks []*TrojanInboundFallback `json:"fallbacks"` } // Build implements Buildable func (c *TrojanServerConfig) Build() (proto.Message, error) { - config := &trojan.ServerConfig{ - Users: make([]*protocol.User, len(c.Clients)), - } - + config := new(trojan.ServerConfig) + config.Users = make([]*protocol.User, len(c.Clients)) for idx, rawUser := range c.Clients { - if rawUser.Flow != "" { - return nil, errors.PrintRemovedFeatureError(`Flow for Trojan`, ``) + user := new(protocol.User) + account := &trojan.Account{ + Password: rawUser.Password, + Flow: rawUser.Flow, } - config.Users[idx] = &protocol.User{ - Level: uint32(rawUser.Level), - Email: rawUser.Email, - Account: serial.ToTypedMessage(&trojan.Account{ - Password: rawUser.Password, - }), + switch account.Flow { + case "": + default: + return nil, newError(`Trojan clients: "flow" doesn't support "` + account.Flow + `" in this version`) } + + user.Email = rawUser.Email + user.Level = uint32(rawUser.Level) + user.Account = serial.ToTypedMessage(account) + config.Users[idx] = user } + if c.Fallback != nil { + return nil, newError(`Trojan settings: please use "fallbacks":[{}] instead of "fallback":{}`) + } for _, fb := range c.Fallbacks { var i uint16 var s string @@ -137,36 +149,39 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) { for _, fb := range config.Fallbacks { /* if fb.Alpn == "h2" && fb.Path != "" { - return nil, errors.New(`Trojan fallbacks: "alpn":"h2" doesn't support "path"`) + return nil, newError(`Trojan fallbacks: "alpn":"h2" doesn't support "path"`) } */ if fb.Path != "" && fb.Path[0] != '/' { - return nil, errors.New(`Trojan fallbacks: "path" must be empty or start with "/"`) + return nil, newError(`Trojan fallbacks: "path" must be empty or start with "/"`) } if fb.Type == "" && fb.Dest != "" { if fb.Dest == "serve-ws-none" { fb.Type = "serve" - } else if filepath.IsAbs(fb.Dest) || fb.Dest[0] == '@' { - fb.Type = "unix" - if strings.HasPrefix(fb.Dest, "@@") && (runtime.GOOS == "linux" || runtime.GOOS == "android") { - fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy - copy(fullAddr, fb.Dest[1:]) - fb.Dest = string(fullAddr) - } } else { - if _, err := strconv.Atoi(fb.Dest); err == nil { - fb.Dest = "127.0.0.1:" + fb.Dest - } - if _, _, err := net.SplitHostPort(fb.Dest); err == nil { - fb.Type = "tcp" + switch fb.Dest[0] { + case '@', '/': + fb.Type = "unix" + if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") { + fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy + copy(fullAddr, fb.Dest[1:]) + fb.Dest = string(fullAddr) + } + default: + if _, err := strconv.Atoi(fb.Dest); err == nil { + fb.Dest = "127.0.0.1:" + fb.Dest + } + if _, _, err := net.SplitHostPort(fb.Dest); err == nil { + fb.Type = "tcp" + } } } } if fb.Type == "" { - return nil, errors.New(`Trojan fallbacks: please fill in a valid value for every "dest"`) + return nil, newError(`Trojan fallbacks: please fill in a valid value for every "dest"`) } if fb.Xver > 2 { - return nil, errors.New(`Trojan fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) + return nil, newError(`Trojan fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) } } diff --git a/infra/conf/vless.go b/infra/conf/vless.go index 27da68dc..d829cdee 100644 --- a/infra/conf/vless.go +++ b/infra/conf/vless.go @@ -2,13 +2,11 @@ package conf import ( "encoding/json" - "path/filepath" "runtime" "strconv" - "strings" "syscall" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" @@ -16,7 +14,6 @@ import ( "github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless/inbound" "github.com/xtls/xray-core/proxy/vless/outbound" - "google.golang.org/protobuf/proto" ) type VLessInboundFallback struct { @@ -31,6 +28,7 @@ type VLessInboundFallback struct { type VLessInboundConfig struct { Clients []json.RawMessage `json:"clients"` Decryption string `json:"decryption"` + Fallback *VLessInboundFallback `json:"fallback"` Fallbacks []*VLessInboundFallback `json:"fallbacks"` } @@ -41,11 +39,11 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { for idx, rawUser := range c.Clients { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { - return nil, errors.New(`VLESS clients: invalid user`).Base(err) + return nil, newError(`VLESS clients: invalid user`).Base(err) } account := new(vless.Account) if err := json.Unmarshal(rawUser, account); err != nil { - return nil, errors.New(`VLESS clients: invalid user`).Base(err) + return nil, newError(`VLESS clients: invalid user`).Base(err) } u, err := uuid.ParseString(account.Id) @@ -57,11 +55,11 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { switch account.Flow { case "", vless.XRV: default: - return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) + return nil, newError(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) } if account.Encryption != "" { - return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`) + return nil, newError(`VLESS clients: "encryption" should not in inbound settings`) } user.Account = serial.ToTypedMessage(account) @@ -69,10 +67,13 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { } if c.Decryption != "none" { - return nil, errors.New(`VLESS settings: please add/set "decryption":"none" to every settings`) + return nil, newError(`VLESS settings: please add/set "decryption":"none" to every settings`) } config.Decryption = c.Decryption + if c.Fallback != nil { + return nil, newError(`VLESS settings: please use "fallbacks":[{}] instead of "fallback":{}`) + } for _, fb := range c.Fallbacks { var i uint16 var s string @@ -93,36 +94,39 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { for _, fb := range config.Fallbacks { /* if fb.Alpn == "h2" && fb.Path != "" { - return nil, errors.New(`VLESS fallbacks: "alpn":"h2" doesn't support "path"`) + return nil, newError(`VLESS fallbacks: "alpn":"h2" doesn't support "path"`) } */ if fb.Path != "" && fb.Path[0] != '/' { - return nil, errors.New(`VLESS fallbacks: "path" must be empty or start with "/"`) + return nil, newError(`VLESS fallbacks: "path" must be empty or start with "/"`) } if fb.Type == "" && fb.Dest != "" { if fb.Dest == "serve-ws-none" { fb.Type = "serve" - } else if filepath.IsAbs(fb.Dest) || fb.Dest[0] == '@' { - fb.Type = "unix" - if strings.HasPrefix(fb.Dest, "@@") && (runtime.GOOS == "linux" || runtime.GOOS == "android") { - fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy - copy(fullAddr, fb.Dest[1:]) - fb.Dest = string(fullAddr) - } } else { - if _, err := strconv.Atoi(fb.Dest); err == nil { - fb.Dest = "127.0.0.1:" + fb.Dest - } - if _, _, err := net.SplitHostPort(fb.Dest); err == nil { - fb.Type = "tcp" + switch fb.Dest[0] { + case '@', '/': + fb.Type = "unix" + if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") { + fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy + copy(fullAddr, fb.Dest[1:]) + fb.Dest = string(fullAddr) + } + default: + if _, err := strconv.Atoi(fb.Dest); err == nil { + fb.Dest = "127.0.0.1:" + fb.Dest + } + if _, _, err := net.SplitHostPort(fb.Dest); err == nil { + fb.Type = "tcp" + } } } } if fb.Type == "" { - return nil, errors.New(`VLESS fallbacks: please fill in a valid value for every "dest"`) + return nil, newError(`VLESS fallbacks: please fill in a valid value for every "dest"`) } if fb.Xver > 2 { - return nil, errors.New(`VLESS fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) + return nil, newError(`VLESS fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) } } @@ -144,15 +148,15 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) { config := new(outbound.Config) if len(c.Vnext) == 0 { - return nil, errors.New(`VLESS settings: "vnext" is empty`) + return nil, newError(`VLESS settings: "vnext" is empty`) } config.Vnext = make([]*protocol.ServerEndpoint, len(c.Vnext)) for idx, rec := range c.Vnext { if rec.Address == nil { - return nil, errors.New(`VLESS vnext: "address" is not set`) + return nil, newError(`VLESS vnext: "address" is not set`) } if len(rec.Users) == 0 { - return nil, errors.New(`VLESS vnext: "users" is empty`) + return nil, newError(`VLESS vnext: "users" is empty`) } spec := &protocol.ServerEndpoint{ Address: rec.Address.Build(), @@ -162,11 +166,11 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) { for idx, rawUser := range rec.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { - return nil, errors.New(`VLESS users: invalid user`).Base(err) + return nil, newError(`VLESS users: invalid user`).Base(err) } account := new(vless.Account) if err := json.Unmarshal(rawUser, account); err != nil { - return nil, errors.New(`VLESS users: invalid user`).Base(err) + return nil, newError(`VLESS users: invalid user`).Base(err) } u, err := uuid.ParseString(account.Id) @@ -178,11 +182,11 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) { switch account.Flow { case "", vless.XRV, vless.XRV + "-udp443": default: - return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`) + return nil, newError(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`) } if account.Encryption != "none" { - return nil, errors.New(`VLESS users: please add/set "encryption":"none" for every user`) + return nil, newError(`VLESS users: please add/set "encryption":"none" for every user`) } user.Account = serial.ToTypedMessage(account) diff --git a/infra/conf/vmess.go b/infra/conf/vmess.go index 90ab92d4..c646bebb 100644 --- a/infra/conf/vmess.go +++ b/infra/conf/vmess.go @@ -4,18 +4,18 @@ import ( "encoding/json" "strings" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/proxy/vmess" "github.com/xtls/xray-core/proxy/vmess/inbound" "github.com/xtls/xray-core/proxy/vmess/outbound" - "google.golang.org/protobuf/proto" ) type VMessAccount struct { ID string `json:"id"` + AlterIds uint16 `json:"alterId"` Security string `json:"security"` Experiments string `json:"experiments"` } @@ -38,7 +38,8 @@ func (a *VMessAccount) Build() *vmess.Account { st = protocol.SecurityType_AUTO } return &vmess.Account{ - Id: a.ID, + Id: a.ID, + AlterId: uint32(a.AlterIds), SecuritySettings: &protocol.SecurityConfig{ Type: st, }, @@ -57,26 +58,36 @@ func (c *VMessDetourConfig) Build() *inbound.DetourConfig { } } +type FeaturesConfig struct { + Detour *VMessDetourConfig `json:"detour"` +} + type VMessDefaultConfig struct { - Level byte `json:"level"` + AlterIDs uint16 `json:"alterId"` + Level byte `json:"level"` } // Build implements Buildable func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig { config := new(inbound.DefaultConfig) + config.AlterId = uint32(c.AlterIDs) config.Level = uint32(c.Level) return config } type VMessInboundConfig struct { Users []json.RawMessage `json:"clients"` + Features *FeaturesConfig `json:"features"` Defaults *VMessDefaultConfig `json:"default"` DetourConfig *VMessDetourConfig `json:"detour"` + SecureOnly bool `json:"disableInsecureEncryption"` } // Build implements Buildable func (c *VMessInboundConfig) Build() (proto.Message, error) { - config := &inbound.Config{} + config := &inbound.Config{ + SecureEncryptionOnly: c.SecureOnly, + } if c.Defaults != nil { config.Default = c.Defaults.Build() @@ -84,17 +95,19 @@ func (c *VMessInboundConfig) Build() (proto.Message, error) { if c.DetourConfig != nil { config.Detour = c.DetourConfig.Build() + } else if c.Features != nil && c.Features.Detour != nil { + config.Detour = c.Features.Detour.Build() } config.User = make([]*protocol.User, len(c.Users)) for idx, rawData := range c.Users { user := new(protocol.User) if err := json.Unmarshal(rawData, user); err != nil { - return nil, errors.New("invalid VMess user").Base(err) + return nil, newError("invalid VMess user").Base(err) } account := new(VMessAccount) if err := json.Unmarshal(rawData, account); err != nil { - return nil, errors.New("invalid VMess user").Base(err) + return nil, newError("invalid VMess user").Base(err) } u, err := uuid.ParseString(account.ID) @@ -125,15 +138,15 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) { config := new(outbound.Config) if len(c.Receivers) == 0 { - return nil, errors.New("0 VMess receiver configured") + return nil, newError("0 VMess receiver configured") } serverSpecs := make([]*protocol.ServerEndpoint, len(c.Receivers)) for idx, rec := range c.Receivers { if len(rec.Users) == 0 { - return nil, errors.New("0 user configured for VMess outbound") + return nil, newError("0 user configured for VMess outbound") } if rec.Address == nil { - return nil, errors.New("address is not set in VMess outbound config") + return nil, newError("address is not set in VMess outbound config") } spec := &protocol.ServerEndpoint{ Address: rec.Address.Build(), @@ -142,11 +155,11 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) { for _, rawUser := range rec.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { - return nil, errors.New("invalid VMess user").Base(err) + return nil, newError("invalid VMess user").Base(err) } account := new(VMessAccount) if err := json.Unmarshal(rawUser, account); err != nil { - return nil, errors.New("invalid VMess user").Base(err) + return nil, newError("invalid VMess user").Base(err) } u, err := uuid.ParseString(account.ID) diff --git a/infra/conf/vmess_test.go b/infra/conf/vmess_test.go index 8adda170..17cda04d 100644 --- a/infra/conf/vmess_test.go +++ b/infra/conf/vmess_test.go @@ -105,6 +105,7 @@ func TestVMessInbound(t *testing.T) { Detour: &inbound.DetourConfig{ To: "tag_to_detour", }, + SecureEncryptionOnly: true, }, }, }) diff --git a/infra/conf/wireguard.go b/infra/conf/wireguard.go index 34ce7215..6b102b14 100644 --- a/infra/conf/wireguard.go +++ b/infra/conf/wireguard.go @@ -3,18 +3,16 @@ package conf import ( "encoding/base64" "encoding/hex" - "strings" - "github.com/xtls/xray-core/common/errors" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/proxy/wireguard" - "google.golang.org/protobuf/proto" ) type WireGuardPeerConfig struct { PublicKey string `json:"publicKey"` PreSharedKey string `json:"preSharedKey"` Endpoint string `json:"endpoint"` - KeepAlive uint32 `json:"keepAlive"` + KeepAlive int `json:"keepAlive"` AllowedIPs []string `json:"allowedIPs,omitempty"` } @@ -22,23 +20,23 @@ func (c *WireGuardPeerConfig) Build() (proto.Message, error) { var err error config := new(wireguard.PeerConfig) - if c.PublicKey != "" { - config.PublicKey, err = ParseWireGuardKey(c.PublicKey) - if err != nil { - return nil, err - } + config.PublicKey, err = parseWireGuardKey(c.PublicKey) + if err != nil { + return nil, err } if c.PreSharedKey != "" { - config.PreSharedKey, err = ParseWireGuardKey(c.PreSharedKey) + config.PreSharedKey, err = parseWireGuardKey(c.PreSharedKey) if err != nil { return nil, err } + } else { + config.PreSharedKey = "0000000000000000000000000000000000000000000000000000000000000000" } config.Endpoint = c.Endpoint // default 0 - config.KeepAlive = c.KeepAlive + config.KeepAlive = int32(c.KeepAlive) if c.AllowedIPs == nil { config.AllowedIps = []string{"0.0.0.0/0", "::0/0"} } else { @@ -49,25 +47,21 @@ func (c *WireGuardPeerConfig) Build() (proto.Message, error) { } type WireGuardConfig struct { - IsClient bool `json:""` - - NoKernelTun bool `json:"noKernelTun"` - SecretKey string `json:"secretKey"` - Address []string `json:"address"` - Peers []*WireGuardPeerConfig `json:"peers"` - MTU int32 `json:"mtu"` - NumWorkers int32 `json:"workers"` - Reserved []byte `json:"reserved"` - DomainStrategy string `json:"domainStrategy"` + SecretKey string `json:"secretKey"` + Address []string `json:"address"` + Peers []*WireGuardPeerConfig `json:"peers"` + MTU int `json:"mtu"` + NumWorkers int `json:"workers"` + Reserved []byte `json:"reserved"` } func (c *WireGuardConfig) Build() (proto.Message, error) { config := new(wireguard.DeviceConfig) var err error - config.SecretKey, err = ParseWireGuardKey(c.SecretKey) + config.SecretKey, err = parseWireGuardKey(c.SecretKey) if err != nil { - return nil, errors.New("invalid WireGuard secret key: %w", err) + return nil, err } if c.Address == nil { @@ -91,62 +85,33 @@ func (c *WireGuardConfig) Build() (proto.Message, error) { if c.MTU == 0 { config.Mtu = 1420 } else { - config.Mtu = c.MTU + config.Mtu = int32(c.MTU) } - // these a fallback code exists in wireguard-go code, + // these a fallback code exists in github.com/nanoda0523/wireguard-go code, // we don't need to process fallback manually - config.NumWorkers = c.NumWorkers + config.NumWorkers = int32(c.NumWorkers) if len(c.Reserved) != 0 && len(c.Reserved) != 3 { - return nil, errors.New(`"reserved" should be empty or 3 bytes`) + return nil, newError(`"reserved" should be empty or 3 bytes`) } config.Reserved = c.Reserved - switch strings.ToLower(c.DomainStrategy) { - case "forceip", "": - config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP - case "forceipv4": - config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP4 - case "forceipv6": - config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP6 - case "forceipv4v6": - config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP46 - case "forceipv6v4": - config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP64 - default: - return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy) - } - - config.IsClient = c.IsClient - config.NoKernelTun = c.NoKernelTun - return config, nil } -func ParseWireGuardKey(str string) (string, error) { - var err error - - if str == "" { - return "", errors.New("key must not be empty") - } - - if len(str)%2 == 0 { - _, err = hex.DecodeString(str) - if err == nil { - return str, nil +func parseWireGuardKey(str string) (string, error) { + if len(str) != 64 { + // may in base64 form + dat, err := base64.StdEncoding.DecodeString(str) + if err != nil { + return "", err } - } - - var dat []byte - str = strings.TrimSuffix(str, "=") - if strings.ContainsRune(str, '+') || strings.ContainsRune(str, '/') { - dat, err = base64.RawStdEncoding.DecodeString(str) + if len(dat) != 32 { + return "", newError("key should be 32 bytes: " + str) + } + return hex.EncodeToString(dat), err } else { - dat, err = base64.RawURLEncoding.DecodeString(str) + // already hex form + return str, nil } - if err == nil { - return hex.EncodeToString(dat), nil - } - - return "", errors.New("failed to deserialize key").Base(err) } diff --git a/infra/conf/wireguard_test.go b/infra/conf/wireguard_test.go index c4c24c44..f0136bf0 100644 --- a/infra/conf/wireguard_test.go +++ b/infra/conf/wireguard_test.go @@ -7,7 +7,7 @@ import ( "github.com/xtls/xray-core/proxy/wireguard" ) -func TestWireGuardConfig(t *testing.T) { +func TestWireGuardOutbound(t *testing.T) { creator := func() Buildable { return new(WireGuardConfig) } @@ -24,9 +24,7 @@ func TestWireGuardConfig(t *testing.T) { } ], "mtu": 1300, - "workers": 2, - "domainStrategy": "ForceIPv6v4", - "noKernelTun": false + "workers": 2 }`, Parser: loadJSON(creator), Output: &wireguard.DeviceConfig{ @@ -36,16 +34,15 @@ func TestWireGuardConfig(t *testing.T) { Peers: []*wireguard.PeerConfig{ { // also can read from hex form directly - PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a", - Endpoint: "127.0.0.1:1234", - KeepAlive: 0, - AllowedIps: []string{"0.0.0.0/0", "::0/0"}, + PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a", + PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000", + Endpoint: "127.0.0.1:1234", + KeepAlive: 0, + AllowedIps: []string{"0.0.0.0/0", "::0/0"}, }, }, - Mtu: 1300, - NumWorkers: 2, - DomainStrategy: wireguard.DeviceConfig_FORCE_IP64, - NoKernelTun: false, + Mtu: 1300, + NumWorkers: 2, }, }, }) diff --git a/infra/conf/xray.go b/infra/conf/xray.go index f1d9fb08..949e5534 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -1,19 +1,15 @@ package conf import ( - "context" "encoding/json" "fmt" "log" "os" - "path/filepath" "strings" "github.com/xtls/xray-core/app/dispatcher" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/stats" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/serial" core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/transport/internet" @@ -24,12 +20,11 @@ var ( "dokodemo-door": func() interface{} { return new(DokodemoConfig) }, "http": func() interface{} { return new(HTTPServerConfig) }, "shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) }, - "mixed": func() interface{} { return new(SocksServerConfig) }, "socks": func() interface{} { return new(SocksServerConfig) }, "vless": func() interface{} { return new(VLessInboundConfig) }, "vmess": func() interface{} { return new(VMessInboundConfig) }, "trojan": func() interface{} { return new(TrojanServerConfig) }, - "wireguard": func() interface{} { return &WireGuardConfig{IsClient: false} }, + "mtproto": func() interface{} { return new(MTProtoServerConfig) }, }, "protocol", "settings") outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{ @@ -42,13 +37,29 @@ var ( "vless": func() interface{} { return new(VLessOutboundConfig) }, "vmess": func() interface{} { return new(VMessOutboundConfig) }, "trojan": func() interface{} { return new(TrojanClientConfig) }, + "mtproto": func() interface{} { return new(MTProtoClientConfig) }, "dns": func() interface{} { return new(DNSOutboundConfig) }, - "wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} }, + "wireguard": func() interface{} { return new(WireGuardConfig) }, }, "protocol", "settings") ctllog = log.New(os.Stderr, "xctl> ", 0) ) +func toProtocolList(s []string) ([]proxyman.KnownProtocols, error) { + kp := make([]proxyman.KnownProtocols, 0, 8) + for _, p := range s { + switch strings.ToLower(p) { + case "http": + kp = append(kp, proxyman.KnownProtocols_HTTP) + case "https", "tls", "ssl": + kp = append(kp, proxyman.KnownProtocols_TLS) + default: + return nil, newError("Unknown protocol: ", p) + } + } + return kp, nil +} + type SniffingConfig struct { Enabled bool `json:"enabled"` DestOverride *StringList `json:"destOverride"` @@ -69,10 +80,12 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) { p = append(p, "tls") case "quic": p = append(p, "quic") - case "fakedns", "fakedns+others": + case "fakedns": p = append(p, "fakedns") + case "fakedns+others": + p = append(p, "fakedns+others") default: - return nil, errors.New("unknown protocol: ", protocol) + return nil, newError("unknown protocol: ", protocol) } } } @@ -94,27 +107,25 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) { } type MuxConfig struct { - Enabled bool `json:"enabled"` - Concurrency int16 `json:"concurrency"` - XudpConcurrency int16 `json:"xudpConcurrency"` - XudpProxyUDP443 string `json:"xudpProxyUDP443"` + Enabled bool `json:"enabled"` + Concurrency int16 `json:"concurrency"` } // Build creates MultiplexingConfig, Concurrency < 0 completely disables mux. -func (m *MuxConfig) Build() (*proxyman.MultiplexingConfig, error) { - switch m.XudpProxyUDP443 { - case "": - m.XudpProxyUDP443 = "reject" - case "reject", "allow", "skip": - default: - return nil, errors.New(`unknown "xudpProxyUDP443": `, m.XudpProxyUDP443) +func (m *MuxConfig) Build() *proxyman.MultiplexingConfig { + if m.Concurrency < 0 { + return nil } + + var con uint32 = 8 + if m.Concurrency > 0 { + con = uint32(m.Concurrency) + } + return &proxyman.MultiplexingConfig{ - Enabled: m.Enabled, - Concurrency: int32(m.Concurrency), - XudpConcurrency: int32(m.XudpConcurrency), - XudpProxyUDP443: m.XudpProxyUDP443, - }, nil + Enabled: m.Enabled, + Concurrency: con, + } } type InboundDetourAllocationConfig struct { @@ -134,7 +145,7 @@ func (c *InboundDetourAllocationConfig) Build() (*proxyman.AllocationStrategy, e case "external": config.Type = proxyman.AllocationStrategy_External default: - return nil, errors.New("unknown allocation strategy: ", c.Strategy) + return nil, newError("unknown allocation strategy: ", c.Strategy) } if c.Concurrency != nil { config.Concurrency = &proxyman.AllocationStrategy_AllocationStrategyConcurrency{ @@ -159,6 +170,7 @@ type InboundDetourConfig struct { Tag string `json:"tag"` Allocation *InboundDetourAllocationConfig `json:"allocate"` StreamSetting *StreamConfig `json:"streamSettings"` + DomainOverride *StringList `json:"domainOverride"` SniffingConfig *SniffingConfig `json:"sniffing"` } @@ -169,18 +181,18 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { if c.ListenOn == nil { // Listen on anyip, must set PortList if c.PortList == nil { - return nil, errors.New("Listen on AnyIP but no Port(s) set in InboundDetour.") + return nil, newError("Listen on AnyIP but no Port(s) set in InboundDetour.") } receiverSettings.PortList = c.PortList.Build() } else { // Listen on specific IP or Unix Domain Socket receiverSettings.Listen = c.ListenOn.Build() - listenDS := c.ListenOn.Family().IsDomain() && (filepath.IsAbs(c.ListenOn.Domain()) || c.ListenOn.Domain()[0] == '@') + listenDS := c.ListenOn.Family().IsDomain() && (c.ListenOn.Domain()[0] == '/' || c.ListenOn.Domain()[0] == '@') listenIP := c.ListenOn.Family().IsIP() || (c.ListenOn.Family().IsDomain() && c.ListenOn.Domain() == "localhost") if listenIP { // Listen on specific IP, must set PortList if c.PortList == nil { - return nil, errors.New("Listen on specific ip without port in InboundDetour.") + return nil, newError("Listen on specific ip without port in InboundDetour.") } // Listen on IP:Port receiverSettings.PortList = c.PortList.Build() @@ -190,7 +202,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { receiverSettings.PortList = nil } } else { - return nil, errors.New("unable to listen on domain address: ", c.ListenOn.Domain()) + return nil, newError("unable to listen on domain address: ", c.ListenOn.Domain()) } } @@ -209,7 +221,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { for _, pr := range c.PortList.Range { fmt.Fprintf(&ports, "%d-%d ", pr.From, pr.To) } - return nil, errors.New("not enough ports. concurrency = ", concurrency, " ports: ", ports.String()) + return nil, newError("not enough ports. concurrency = ", concurrency, " ports: ", ports.String()) } as, err := c.Allocation.Build() @@ -228,10 +240,17 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { if c.SniffingConfig != nil { s, err := c.SniffingConfig.Build() if err != nil { - return nil, errors.New("failed to build sniffing config").Base(err) + return nil, newError("failed to build sniffing config").Base(err) } receiverSettings.SniffingSettings = s } + if c.DomainOverride != nil { + kp, err := toProtocolList(*c.DomainOverride) + if err != nil { + return nil, newError("failed to parse inbound detour config").Base(err) + } + receiverSettings.DomainOverride = kp + } settings := []byte("{}") if c.Settings != nil { @@ -239,14 +258,14 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { } rawConfig, err := inboundConfigLoader.LoadWithID(settings, c.Protocol) if err != nil { - return nil, errors.New("failed to load inbound detour config for protocol ", c.Protocol).Base(err) + return nil, newError("failed to load inbound detour config.").Base(err) } if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok { receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect } ts, err := rawConfig.(Buildable).Build() if err != nil { - return nil, errors.New("failed to build inbound handler for protocol ", c.Protocol).Base(err) + return nil, err } return &core.InboundHandlerConfig{ @@ -258,7 +277,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { type OutboundDetourConfig struct { Protocol string `json:"protocol"` - SendThrough *string `json:"sendThrough"` + SendThrough *Address `json:"sendThrough"` Tag string `json:"tag"` Settings *json.RawMessage `json:"settings"` StreamSetting *StreamConfig `json:"streamSettings"` @@ -271,7 +290,7 @@ func (c *OutboundDetourConfig) checkChainProxyConfig() error { return nil } if len(c.ProxySettings.Tag) > 0 && len(c.StreamSetting.SocketSettings.DialerProxy) > 0 { - return errors.New("proxySettings.tag is conflicted with sockopt.dialerProxy").AtWarning() + return newError("proxySettings.tag is conflicted with sockopt.dialerProxy").AtWarning() } return nil } @@ -284,17 +303,9 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { } if c.SendThrough != nil { - address := ParseSendThough(c.SendThrough) - //Check if CIDR exists - if strings.Contains(*c.SendThrough, "/") { - senderSettings.ViaCidr = strings.Split(*c.SendThrough, "/")[1] - } else { - if address.Family().IsDomain() { - domain := address.Address.Domain() - if domain != "origin" && domain != "srcip" { - return nil, errors.New("unable to send through: " + address.String()) - } - } + address := c.SendThrough + if address.Family().IsDomain() { + return nil, newError("unable to send through: " + address.String()) } senderSettings.Via = address.Build() } @@ -302,7 +313,7 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { if c.StreamSetting != nil { ss, err := c.StreamSetting.Build() if err != nil { - return nil, errors.New("failed to build stream settings for outbound detour").Base(err) + return nil, err } senderSettings.StreamSettings = ss } @@ -310,7 +321,7 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { if c.ProxySettings != nil { ps, err := c.ProxySettings.Build() if err != nil { - return nil, errors.New("invalid outbound detour proxy settings").Base(err) + return nil, newError("invalid outbound detour proxy settings.").Base(err) } if ps.TransportLayerProxy { if senderSettings.StreamSettings != nil { @@ -328,11 +339,7 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { } if c.MuxSettings != nil { - ms, err := c.MuxSettings.Build() - if err != nil { - return nil, errors.New("failed to build Mux config").Base(err) - } - senderSettings.MultiplexSettings = ms + senderSettings.MultiplexSettings = c.MuxSettings.Build() } settings := []byte("{}") @@ -341,11 +348,11 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { } rawConfig, err := outboundConfigLoader.LoadWithID(settings, c.Protocol) if err != nil { - return nil, errors.New("failed to load outbound detour config for protocol ", c.Protocol).Base(err) + return nil, newError("failed to parse to outbound detour config.").Base(err) } ts, err := rawConfig.(Buildable).Build() if err != nil { - return nil, errors.New("failed to build outbound handler for protocol ", c.Protocol).Base(err) + return nil, err } return &core.OutboundHandlerConfig{ @@ -363,23 +370,40 @@ func (c *StatsConfig) Build() (*stats.Config, error) { } type Config struct { - // Deprecated: Global transport config is no longer used - // left for returning error - Transport map[string]json.RawMessage `json:"transport"` + // Port of this Point server. + // Deprecated: Port exists for historical compatibility + // and should not be used. + Port uint16 `json:"port"` - LogConfig *LogConfig `json:"log"` - RouterConfig *RouterConfig `json:"routing"` - DNSConfig *DNSConfig `json:"dns"` - InboundConfigs []InboundDetourConfig `json:"inbounds"` - OutboundConfigs []OutboundDetourConfig `json:"outbounds"` - Policy *PolicyConfig `json:"policy"` - API *APIConfig `json:"api"` - Metrics *MetricsConfig `json:"metrics"` - Stats *StatsConfig `json:"stats"` - Reverse *ReverseConfig `json:"reverse"` - FakeDNS *FakeDNSConfig `json:"fakeDns"` - Observatory *ObservatoryConfig `json:"observatory"` - BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"` + // Deprecated: InboundConfig exists for historical compatibility + // and should not be used. + InboundConfig *InboundDetourConfig `json:"inbound"` + + // Deprecated: OutboundConfig exists for historical compatibility + // and should not be used. + OutboundConfig *OutboundDetourConfig `json:"outbound"` + + // Deprecated: InboundDetours exists for historical compatibility + // and should not be used. + InboundDetours []InboundDetourConfig `json:"inboundDetour"` + + // Deprecated: OutboundDetours exists for historical compatibility + // and should not be used. + OutboundDetours []OutboundDetourConfig `json:"outboundDetour"` + + LogConfig *LogConfig `json:"log"` + RouterConfig *RouterConfig `json:"routing"` + DNSConfig *DNSConfig `json:"dns"` + InboundConfigs []InboundDetourConfig `json:"inbounds"` + OutboundConfigs []OutboundDetourConfig `json:"outbounds"` + Transport *TransportConfig `json:"transport"` + Policy *PolicyConfig `json:"policy"` + API *APIConfig `json:"api"` + Metrics *MetricsConfig `json:"metrics"` + Stats *StatsConfig `json:"stats"` + Reverse *ReverseConfig `json:"reverse"` + FakeDNS *FakeDNSConfig `json:"fakeDns"` + Observatory *ObservatoryConfig `json:"observatory"` } func (c *Config) findInboundTag(tag string) int { @@ -444,52 +468,79 @@ func (c *Config) Override(o *Config, fn string) { c.Observatory = o.Observatory } - if o.BurstObservatory != nil { - c.BurstObservatory = o.BurstObservatory + // deprecated attrs... keep them for now + if o.InboundConfig != nil { + c.InboundConfig = o.InboundConfig } + if o.OutboundConfig != nil { + c.OutboundConfig = o.OutboundConfig + } + if o.InboundDetours != nil { + c.InboundDetours = o.InboundDetours + } + if o.OutboundDetours != nil { + c.OutboundDetours = o.OutboundDetours + } + // deprecated attrs - // update the Inbound in slice if the only one in override config has same tag + // update the Inbound in slice if the only one in overide config has same tag if len(o.InboundConfigs) > 0 { - for i := range o.InboundConfigs { - if idx := c.findInboundTag(o.InboundConfigs[i].Tag); idx > -1 { - c.InboundConfigs[idx] = o.InboundConfigs[i] - errors.LogInfo(context.Background(), "[", fn, "] updated inbound with tag: ", o.InboundConfigs[i].Tag) - + if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 { + if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 { + c.InboundConfigs[idx] = o.InboundConfigs[0] + ctllog.Println("[", fn, "] updated inbound with tag: ", o.InboundConfigs[0].Tag) } else { - c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[i]) - errors.LogInfo(context.Background(), "[", fn, "] appended inbound with tag: ", o.InboundConfigs[i].Tag) + c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0]) + ctllog.Println("[", fn, "] appended inbound with tag: ", o.InboundConfigs[0].Tag) } - + } else { + c.InboundConfigs = o.InboundConfigs } } - // update the Outbound in slice if the only one in override config has same tag + // update the Outbound in slice if the only one in overide config has same tag if len(o.OutboundConfigs) > 0 { - outboundPrepends := []OutboundDetourConfig{} - for i := range o.OutboundConfigs { - if idx := c.findOutboundTag(o.OutboundConfigs[i].Tag); idx > -1 { - c.OutboundConfigs[idx] = o.OutboundConfigs[i] - errors.LogInfo(context.Background(), "[", fn, "] updated outbound with tag: ", o.OutboundConfigs[i].Tag) + if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 { + if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 { + c.OutboundConfigs[idx] = o.OutboundConfigs[0] + ctllog.Println("[", fn, "] updated outbound with tag: ", o.OutboundConfigs[0].Tag) } else { if strings.Contains(strings.ToLower(fn), "tail") { - c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[i]) - errors.LogInfo(context.Background(), "[", fn, "] appended outbound with tag: ", o.OutboundConfigs[i].Tag) + c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0]) + ctllog.Println("[", fn, "] appended outbound with tag: ", o.OutboundConfigs[0].Tag) } else { - outboundPrepends = append(outboundPrepends, o.OutboundConfigs[i]) - errors.LogInfo(context.Background(), "[", fn, "] prepend outbound with tag: ", o.OutboundConfigs[i].Tag) + c.OutboundConfigs = append(o.OutboundConfigs, c.OutboundConfigs...) + ctllog.Println("[", fn, "] prepended outbound with tag: ", o.OutboundConfigs[0].Tag) } } + } else { + c.OutboundConfigs = o.OutboundConfigs } - if !strings.Contains(strings.ToLower(fn), "tail") && len(outboundPrepends) > 0 { - c.OutboundConfigs = append(outboundPrepends, c.OutboundConfigs...) - } + } +} + +func applyTransportConfig(s *StreamConfig, t *TransportConfig) { + if s.TCPSettings == nil { + s.TCPSettings = t.TCPConfig + } + if s.KCPSettings == nil { + s.KCPSettings = t.KCPConfig + } + if s.WSSettings == nil { + s.WSSettings = t.WSConfig + } + if s.HTTPSettings == nil { + s.HTTPSettings = t.HTTPConfig + } + if s.DSSettings == nil { + s.DSSettings = t.DSConfig } } // Build implements Buildable. func (c *Config) Build() (*core.Config, error) { if err := PostProcessConfigureFile(c); err != nil { - return nil, errors.New("failed to post-process configuration file").Base(err) + return nil, err } config := &core.Config{ @@ -503,21 +554,21 @@ func (c *Config) Build() (*core.Config, error) { if c.API != nil { apiConf, err := c.API.Build() if err != nil { - return nil, errors.New("failed to build API configuration").Base(err) + return nil, err } config.App = append(config.App, serial.ToTypedMessage(apiConf)) } if c.Metrics != nil { metricsConf, err := c.Metrics.Build() if err != nil { - return nil, errors.New("failed to build metrics configuration").Base(err) + return nil, err } config.App = append(config.App, serial.ToTypedMessage(metricsConf)) } if c.Stats != nil { statsConf, err := c.Stats.Build() if err != nil { - return nil, errors.New("failed to build stats configuration").Base(err) + return nil, err } config.App = append(config.App, serial.ToTypedMessage(statsConf)) } @@ -535,7 +586,7 @@ func (c *Config) Build() (*core.Config, error) { if c.RouterConfig != nil { routerConfig, err := c.RouterConfig.Build() if err != nil { - return nil, errors.New("failed to build routing configuration").Base(err) + return nil, err } config.App = append(config.App, serial.ToTypedMessage(routerConfig)) } @@ -543,7 +594,7 @@ func (c *Config) Build() (*core.Config, error) { if c.DNSConfig != nil { dnsApp, err := c.DNSConfig.Build() if err != nil { - return nil, errors.New("failed to build DNS configuration").Base(err) + return nil, newError("failed to parse DNS config").Base(err) } config.App = append(config.App, serial.ToTypedMessage(dnsApp)) } @@ -551,7 +602,7 @@ func (c *Config) Build() (*core.Config, error) { if c.Policy != nil { pc, err := c.Policy.Build() if err != nil { - return nil, errors.New("failed to build policy configuration").Base(err) + return nil, err } config.App = append(config.App, serial.ToTypedMessage(pc)) } @@ -559,7 +610,7 @@ func (c *Config) Build() (*core.Config, error) { if c.Reverse != nil { r, err := c.Reverse.Build() if err != nil { - return nil, errors.New("failed to build reverse configuration").Base(err) + return nil, err } config.App = append(config.App, serial.ToTypedMessage(r)) } @@ -567,7 +618,7 @@ func (c *Config) Build() (*core.Config, error) { if c.FakeDNS != nil { r, err := c.FakeDNS.Build() if err != nil { - return nil, errors.New("failed to build fake DNS configuration").Base(err) + return nil, err } config.App = append([]*serial.TypedMessage{serial.ToTypedMessage(r)}, config.App...) } @@ -575,57 +626,74 @@ func (c *Config) Build() (*core.Config, error) { if c.Observatory != nil { r, err := c.Observatory.Build() if err != nil { - return nil, errors.New("failed to build observatory configuration").Base(err) - } - config.App = append(config.App, serial.ToTypedMessage(r)) - } - - if c.BurstObservatory != nil { - r, err := c.BurstObservatory.Build() - if err != nil { - return nil, errors.New("failed to build burst observatory configuration").Base(err) + return nil, err } config.App = append(config.App, serial.ToTypedMessage(r)) } var inbounds []InboundDetourConfig + if c.InboundConfig != nil { + inbounds = append(inbounds, *c.InboundConfig) + } + + if len(c.InboundDetours) > 0 { + inbounds = append(inbounds, c.InboundDetours...) + } + if len(c.InboundConfigs) > 0 { inbounds = append(inbounds, c.InboundConfigs...) } - if len(c.Transport) > 0 { - return nil, errors.PrintRemovedFeatureError("Global transport config", "streamSettings in inbounds and outbounds") + // Backward compatibility. + if len(inbounds) > 0 && inbounds[0].PortList == nil && c.Port > 0 { + inbounds[0].PortList = &PortList{[]PortRange{{ + From: uint32(c.Port), + To: uint32(c.Port), + }}} } for _, rawInboundConfig := range inbounds { + if c.Transport != nil { + if rawInboundConfig.StreamSetting == nil { + rawInboundConfig.StreamSetting = &StreamConfig{} + } + applyTransportConfig(rawInboundConfig.StreamSetting, c.Transport) + } ic, err := rawInboundConfig.Build() if err != nil { - return nil, errors.New("failed to build inbound config with tag ", rawInboundConfig.Tag).Base(err) + return nil, err } config.Inbound = append(config.Inbound, ic) } var outbounds []OutboundDetourConfig + if c.OutboundConfig != nil { + outbounds = append(outbounds, *c.OutboundConfig) + } + + if len(c.OutboundDetours) > 0 { + outbounds = append(outbounds, c.OutboundDetours...) + } + if len(c.OutboundConfigs) > 0 { outbounds = append(outbounds, c.OutboundConfigs...) } for _, rawOutboundConfig := range outbounds { + if c.Transport != nil { + if rawOutboundConfig.StreamSetting == nil { + rawOutboundConfig.StreamSetting = &StreamConfig{} + } + applyTransportConfig(rawOutboundConfig.StreamSetting, c.Transport) + } oc, err := rawOutboundConfig.Build() if err != nil { - return nil, errors.New("failed to build outbound config with tag ", rawOutboundConfig.Tag).Base(err) + return nil, err } config.Outbound = append(config.Outbound, oc) } return config, nil } - -// Convert string to Address. -func ParseSendThough(Addr *string) *Address { - var addr Address - addr.Address = net.ParseAddress(strings.Split(*Addr, "/")[0]) - return &addr -} diff --git a/infra/conf/xray_test.go b/infra/conf/xray_test.go index 1c0fff8d..8c8151de 100644 --- a/infra/conf/xray_test.go +++ b/infra/conf/xray_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/golang/protobuf/proto" "github.com/google/go-cmp/cmp" "github.com/xtls/xray-core/app/dispatcher" "github.com/xtls/xray-core/app/log" @@ -17,12 +18,15 @@ import ( "github.com/xtls/xray-core/common/serial" core "github.com/xtls/xray-core/core" . "github.com/xtls/xray-core/infra/conf" + "github.com/xtls/xray-core/proxy/blackhole" + dns_proxy "github.com/xtls/xray-core/proxy/dns" + "github.com/xtls/xray-core/proxy/freedom" "github.com/xtls/xray-core/proxy/vmess" "github.com/xtls/xray-core/proxy/vmess/inbound" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/http" "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/websocket" - "google.golang.org/protobuf/proto" ) func TestXrayConfig(t *testing.T) { @@ -39,16 +43,47 @@ func TestXrayConfig(t *testing.T) { runMultiTestCase(t, []TestCase{ { Input: `{ + "outbound": { + "protocol": "freedom", + "settings": {} + }, "log": { "access": "/var/log/xray/access.log", "loglevel": "error", "error": "/var/log/xray/error.log" }, + "inbound": { + "streamSettings": { + "network": "ws", + "wsSettings": { + "headers": { + "host": "example.domain" + }, + "path": "" + }, + "tlsSettings": { + "alpn": "h2" + }, + "security": "tls" + }, + "protocol": "vmess", + "port": 443, + "settings": { + "clients": [ + { + "security": "aes-128-gcm", + "id": "0cdf8a45-303d-4fed-9780-29aa7f54175e" + } + ] + } + }, "inbounds": [{ "streamSettings": { "network": "ws", "wsSettings": { - "host": "example.domain", + "headers": { + "host": "example.domain" + }, "path": "" }, "tlsSettings": { @@ -71,16 +106,33 @@ func TestXrayConfig(t *testing.T) { ] } }], + "outboundDetour": [ + { + "tag": "blocked", + "protocol": "blackhole" + }, + { + "protocol": "dns" + } + ], "routing": { - "rules": [ - { - "ip": [ - "10.0.0.0/8" - ], - "type": "field", - "outboundTag": "blocked" - } - ] + "strategy": "rules", + "settings": { + "rules": [ + { + "ip": [ + "10.0.0.0/8" + ], + "type": "field", + "outboundTag": "blocked" + } + ] + } + }, + "transport": { + "httpSettings": { + "path": "/test" + } } }`, Parser: createParser(), @@ -117,7 +169,109 @@ func TestXrayConfig(t *testing.T) { }, }), }, + Outbound: []*core.OutboundHandlerConfig{ + { + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + ProtocolName: "tcp", + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "http", + Settings: serial.ToTypedMessage(&http.Config{ + Path: "/test", + }), + }, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&freedom.Config{ + DomainStrategy: freedom.Config_AS_IS, + UserLevel: 0, + }), + }, + { + Tag: "blocked", + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + ProtocolName: "tcp", + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "http", + Settings: serial.ToTypedMessage(&http.Config{ + Path: "/test", + }), + }, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), + }, + { + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + ProtocolName: "tcp", + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "http", + Settings: serial.ToTypedMessage(&http.Config{ + Path: "/test", + }), + }, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{ + Server: &net.Endpoint{}, + }), + }, + }, Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(443)}}, + StreamSettings: &internet.StreamConfig{ + ProtocolName: "websocket", + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "websocket", + Settings: serial.ToTypedMessage(&websocket.Config{ + Header: []*websocket.Header{ + { + Key: "host", + Value: "example.domain", + }, + }, + }), + }, + { + ProtocolName: "http", + Settings: serial.ToTypedMessage(&http.Config{ + Path: "/test", + }), + }, + }, + SecurityType: "xray.transport.internet.tls.Config", + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + NextProtocol: []string{"h2"}, + }), + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Level: 0, + Account: serial.ToTypedMessage(&vmess.Account{ + Id: "0cdf8a45-303d-4fed-9780-29aa7f54175e", + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }), + }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortList: &net.PortList{Range: []*net.PortRange{{ @@ -136,7 +290,18 @@ func TestXrayConfig(t *testing.T) { { ProtocolName: "websocket", Settings: serial.ToTypedMessage(&websocket.Config{ - Host: "example.domain", + Header: []*websocket.Header{ + { + Key: "host", + Value: "example.domain", + }, + }, + }), + }, + { + ProtocolName: "http", + Settings: serial.ToTypedMessage(&http.Config{ + Path: "/test", }), }, }, @@ -175,35 +340,24 @@ func TestMuxConfig_Build(t *testing.T) { want *proxyman.MultiplexingConfig }{ {"default", `{"enabled": true, "concurrency": 16}`, &proxyman.MultiplexingConfig{ - Enabled: true, - Concurrency: 16, - XudpConcurrency: 0, - XudpProxyUDP443: "reject", + Enabled: true, + Concurrency: 16, }}, {"empty def", `{}`, &proxyman.MultiplexingConfig{ - Enabled: false, - Concurrency: 0, - XudpConcurrency: 0, - XudpProxyUDP443: "reject", + Enabled: false, + Concurrency: 8, }}, {"not enable", `{"enabled": false, "concurrency": 4}`, &proxyman.MultiplexingConfig{ - Enabled: false, - Concurrency: 4, - XudpConcurrency: 0, - XudpProxyUDP443: "reject", - }}, - {"forbidden", `{"enabled": false, "concurrency": -1}`, &proxyman.MultiplexingConfig{ - Enabled: false, - Concurrency: -1, - XudpConcurrency: 0, - XudpProxyUDP443: "reject", + Enabled: false, + Concurrency: 4, }}, + {"forbidden", `{"enabled": false, "concurrency": -1}`, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &MuxConfig{} common.Must(json.Unmarshal([]byte(tt.fields), m)) - if got, _ := m.Build(); !reflect.DeepEqual(got, tt.want) { + if got := m.Build(); !reflect.DeepEqual(got, tt.want) { t.Errorf("MuxConfig.Build() = %v, want %v", got, tt.want) } }) @@ -225,6 +379,7 @@ func TestConfig_Override(t *testing.T) { LogConfig: &LogConfig{}, RouterConfig: &RouterConfig{}, DNSConfig: &DNSConfig{}, + Transport: &TransportConfig{}, Policy: &PolicyConfig{}, API: &APIConfig{}, Stats: &StatsConfig{}, @@ -235,6 +390,7 @@ func TestConfig_Override(t *testing.T) { LogConfig: &LogConfig{}, RouterConfig: &RouterConfig{}, DNSConfig: &DNSConfig{}, + Transport: &TransportConfig{}, Policy: &PolicyConfig{}, API: &APIConfig{}, Stats: &StatsConfig{}, @@ -259,7 +415,7 @@ func TestConfig_Override(t *testing.T) { &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, "", - &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, + &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, }, { "replace/notag-append", @@ -277,10 +433,10 @@ func TestConfig_Override(t *testing.T) { }, { "replace/outbounds-prepend", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}, {Tag: "pos3"}}}, - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}, {Tag: "pos4", Protocol: "kcp"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, "config.json", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos2", Protocol: "kcp"}, {Tag: "pos4", Protocol: "kcp"}, {Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}, {Tag: "pos3"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, }, { "replace/outbounds-append", diff --git a/infra/vformat/main.go b/infra/vformat/main.go index 84c63a42..e676f08a 100644 --- a/infra/vformat/main.go +++ b/infra/vformat/main.go @@ -1,7 +1,6 @@ package main import ( - "errors" "flag" "fmt" "go/build" @@ -19,7 +18,7 @@ var directory = flag.String("pwd", "", "Working directory of Xray vformat.") func envFile() (string, error) { if file := os.Getenv("GOENV"); file != "" { if file == "off" { - return "", errors.New("GOENV=off") + return "", fmt.Errorf("GOENV=off") } return file, nil } @@ -28,7 +27,7 @@ func envFile() (string, error) { return "", err } if dir == "" { - return "", errors.New("missing user-config dir") + return "", fmt.Errorf("missing user-config dir") } return filepath.Join(dir, "go", "env"), nil } @@ -41,7 +40,7 @@ func GetRuntimeEnv(key string) (string, error) { return "", err } if file == "" { - return "", errors.New("missing runtime env file") + return "", fmt.Errorf("missing runtime env file") } var data []byte var runtimeEnv string diff --git a/infra/vprotogen/main.go b/infra/vprotogen/main.go index 2c1e2298..df920751 100644 --- a/infra/vprotogen/main.go +++ b/infra/vprotogen/main.go @@ -1,7 +1,6 @@ package main import ( - "errors" "flag" "fmt" "go/build" @@ -23,7 +22,7 @@ var directory = flag.String("pwd", "", "Working directory of Xray vprotogen.") func envFile() (string, error) { if file := os.Getenv("GOENV"); file != "" { if file == "off" { - return "", errors.New("GOENV=off") + return "", fmt.Errorf("GOENV=off") } return file, nil } @@ -32,7 +31,7 @@ func envFile() (string, error) { return "", err } if dir == "" { - return "", errors.New("missing user-config dir") + return "", fmt.Errorf("missing user-config dir") } return filepath.Join(dir, "go", "env"), nil } @@ -45,7 +44,7 @@ func GetRuntimeEnv(key string) (string, error) { return "", err } if file == "" { - return "", errors.New("missing runtime env file") + return "", fmt.Errorf("missing runtime env file") } var data []byte var runtimeEnv string @@ -89,11 +88,12 @@ func whichProtoc(suffix, targetedVersion string) (string, error) { path, err := exec.LookPath(protoc) if err != nil { - return "", fmt.Errorf(` + errStr := fmt.Sprintf(` Command "%s" not found. Make sure that %s is in your system path or current path. Download %s v%s or later from https://github.com/protocolbuffers/protobuf/releases `, protoc, protoc, protoc, targetedVersion) + return "", fmt.Errorf(errStr) } return path, nil } @@ -101,14 +101,14 @@ Download %s v%s or later from https://github.com/protocolbuffers/protobuf/releas func getProjectProtocVersion(url string) (string, error) { resp, err := http.Get(url) if err != nil { - return "", errors.New("can not get the version of protobuf used in xray project") + return "", fmt.Errorf("can not get the version of protobuf used in xray project") } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return "", errors.New("can not read from body") + return "", fmt.Errorf("can not read from body") } - versionRegexp := regexp.MustCompile(`\/\/\s*protoc\s*v\d+\.(\d+\.\d+)`) + versionRegexp := regexp.MustCompile(`\/\/\s*protoc\s*v(\d+\.\d+\.\d+)`) matched := versionRegexp.FindStringSubmatch(string(body)) return matched[1], nil } @@ -120,7 +120,7 @@ func getInstalledProtocVersion(protocPath string) (string, error) { if cmdErr != nil { return "", cmdErr } - versionRegexp := regexp.MustCompile(`protoc\s*(\d+\.\d+)`) + versionRegexp := regexp.MustCompile(`protoc\s*(\d+\.\d+\.\d+)`) matched := versionRegexp.FindStringSubmatch(string(output)) return matched[1], nil } @@ -174,14 +174,11 @@ func main() { suffix = ".exe" } - /* - targetedVersion, err := getProjectProtocVersion("https://raw.githubusercontent.com/XTLS/Xray-core/HEAD/core/config.pb.go") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - */ - targetedVersion := "" + targetedVersion, err := getProjectProtocVersion("https://raw.githubusercontent.com/xtls/xray-core/HEAD/core/config.pb.go") + if err != nil { + fmt.Println(err) + os.Exit(1) + } protoc, err := whichProtoc(suffix, targetedVersion) if err != nil { diff --git a/main/commands/all/api/api.go b/main/commands/all/api/api.go index 14a7c564..9986329b 100644 --- a/main/commands/all/api/api.go +++ b/main/commands/all/api/api.go @@ -15,20 +15,9 @@ var CmdAPI = &base.Command{ cmdGetStats, cmdQueryStats, cmdSysStats, - cmdBalancerInfo, - cmdBalancerOverride, cmdAddInbounds, cmdAddOutbounds, cmdRemoveInbounds, cmdRemoveOutbounds, - cmdListInbounds, - cmdListOutbounds, - cmdInboundUser, - cmdInboundUserCount, - cmdAddRules, - cmdRemoveRules, - cmdSourceIpBlock, - cmdOnlineStats, - cmdOnlineStatsIpList, }, } diff --git a/main/commands/all/api/balancer_info.go b/main/commands/all/api/balancer_info.go deleted file mode 100644 index f5b6804c..00000000 --- a/main/commands/all/api/balancer_info.go +++ /dev/null @@ -1,108 +0,0 @@ -package api - -import ( - "fmt" - "os" - "strings" - - routerService "github.com/xtls/xray-core/app/router/command" - "github.com/xtls/xray-core/main/commands/base" -) - -// TODO: support "-json" flag for json output -var cmdBalancerInfo = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api bi [--server=127.0.0.1:8080] [balancer]...", - Short: "Retrieve balancer information", - Long: ` -Retrieve information of specified balancers, including health, strategy and selecting. -If no balancer tag specified, information for all balancers is returned. - -> Ensure that "RoutingService" is enabled under "config.api.services" in the server configuration. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 balancer1 balancer2 -`, - Run: executeBalancerInfo, -} - -func executeBalancerInfo(cmd *base.Command, args []string) { - setSharedFlags(cmd) - cmd.Flag.Parse(args) - unnamedArgs := cmd.Flag.Args() - if len(unnamedArgs) == 0 { - fmt.Println("set balancer tag") - unnamedArgs = []string{""} - } - - conn, ctx, close := dialAPIServer() - defer close() - client := routerService.NewRoutingServiceClient(conn) - r := &routerService.GetBalancerInfoRequest{Tag: unnamedArgs[0]} - resp, err := client.GetBalancerInfo(ctx, r) - if err != nil { - base.Fatalf("failed to get health information: %s", err) - } - - if apiJSON { - showJSONResponse(resp) - return - } - - showBalancerInfo(resp.Balancer) - -} - -func showBalancerInfo(b *routerService.BalancerMsg) { - const tableIndent = 4 - sb := new(strings.Builder) - // Override - if b.Override != nil { - sb.WriteString(" - Selecting Override:\n") - for i, s := range []string{b.Override.Target} { - writeRow(sb, tableIndent, i+1, []string{s}, nil) - } - } - // Selects - sb.WriteString(" - Selects:\n") - if b.PrincipleTarget != nil { - for i, o := range b.PrincipleTarget.Tag { - writeRow(sb, tableIndent, i+1, []string{o}, nil) - } - } - os.Stdout.WriteString(sb.String()) -} - -func getColumnFormats(titles []string) []string { - w := make([]string, len(titles)) - for i, t := range titles { - w[i] = fmt.Sprintf("%%-%ds ", len(t)) - } - return w -} - -func writeRow(sb *strings.Builder, indent, index int, values, formats []string) { - if index == 0 { - // title line - sb.WriteString(strings.Repeat(" ", indent+4)) - } else { - sb.WriteString(fmt.Sprintf("%s%-4d", strings.Repeat(" ", indent), index)) - } - for i, v := range values { - format := "%-14s" - if i < len(formats) { - format = formats[i] - } - sb.WriteString(fmt.Sprintf(format, v)) - } - sb.WriteByte('\n') -} diff --git a/main/commands/all/api/balancer_override.go b/main/commands/all/api/balancer_override.go deleted file mode 100644 index 7386b1a5..00000000 --- a/main/commands/all/api/balancer_override.go +++ /dev/null @@ -1,73 +0,0 @@ -package api - -import ( - routerService "github.com/xtls/xray-core/app/router/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdBalancerOverride = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api bo [--server=127.0.0.1:8080] <-b balancer> outboundTag <-r>", - Short: "Override balancer", - Long: ` -Override the selection target of a balancer. - -> Ensure that the "RoutingService" is properly configured under "config.api.services" in the server configuration. - -Once the balancer's selection is overridden: - -- The balancer's selection result will always be outboundTag - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - - -r, -remove - Remove the existing override. - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -b balancer tag - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -b balancer -r -`, - Run: executeBalancerOverride, -} - -func executeBalancerOverride(cmd *base.Command, args []string) { - var ( - balancer string - remove bool - ) - cmd.Flag.StringVar(&balancer, "b", "", "") - cmd.Flag.StringVar(&balancer, "balancer", "", "") - cmd.Flag.BoolVar(&remove, "r", false, "") - cmd.Flag.BoolVar(&remove, "remove", false, "") - setSharedFlags(cmd) - cmd.Flag.Parse(args) - - if balancer == "" { - base.Fatalf("balancer tag not specified") - } - - conn, ctx, close := dialAPIServer() - defer close() - - client := routerService.NewRoutingServiceClient(conn) - target := "" - if !remove { - target = cmd.Flag.Args()[0] - } - r := &routerService.OverrideBalancerTargetRequest{ - BalancerTag: balancer, - Target: target, - } - - _, err := client.OverrideBalancerTarget(ctx, r) - if err != nil { - base.Fatalf("failed to perform balancer health checks: %s", err) - } -} diff --git a/main/commands/all/api/inbound_user.go b/main/commands/all/api/inbound_user.go deleted file mode 100644 index 23c191d5..00000000 --- a/main/commands/all/api/inbound_user.go +++ /dev/null @@ -1,58 +0,0 @@ -package api - -import ( - handlerService "github.com/xtls/xray-core/app/proxyman/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdInboundUser = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api inbounduser [--server=127.0.0.1:8080] -tag=tag [-email=email]", - Short: "Retrieve inbound user(s)", - Long: ` -Get User info from an inbound. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - - -tag - Inbound tag - - -email - The user's email address. If not provided, all users will be retrieved. - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name" - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name" -email="xray@love.com" -`, - Run: executeInboundUser, -} - -func executeInboundUser(cmd *base.Command, args []string) { - setSharedFlags(cmd) - var tag string - var email string - cmd.Flag.StringVar(&tag, "tag", "", "") - cmd.Flag.StringVar(&email, "email", "", "") - cmd.Flag.Parse(args) - - conn, ctx, close := dialAPIServer() - defer close() - - client := handlerService.NewHandlerServiceClient(conn) - r := &handlerService.GetInboundUserRequest{ - Tag: tag, - Email: email, - } - resp, err := client.GetInboundUsers(ctx, r) - if err != nil { - base.Fatalf("failed to get inbound user: %s", err) - } - showJSONResponse(resp) -} diff --git a/main/commands/all/api/inbound_user_count.go b/main/commands/all/api/inbound_user_count.go deleted file mode 100644 index 8c7c0fc3..00000000 --- a/main/commands/all/api/inbound_user_count.go +++ /dev/null @@ -1,51 +0,0 @@ -package api - -import ( - handlerService "github.com/xtls/xray-core/app/proxyman/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdInboundUserCount = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api inboundusercount [--server=127.0.0.1:8080] -tag=tag", - Short: "Retrieve inbound user count", - Long: ` -Retrieve the user count for a specified inbound tag. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - - -tag - Inbound tag - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name" -`, - Run: executeInboundUserCount, -} - -func executeInboundUserCount(cmd *base.Command, args []string) { - setSharedFlags(cmd) - var tag string - cmd.Flag.StringVar(&tag, "tag", "", "") - cmd.Flag.Parse(args) - - conn, ctx, close := dialAPIServer() - defer close() - - client := handlerService.NewHandlerServiceClient(conn) - r := &handlerService.GetInboundUserRequest{ - Tag: tag, - } - resp, err := client.GetInboundUsersCount(ctx, r) - if err != nil { - base.Fatalf("failed to get inbound user count: %s", err) - } - showJSONResponse(resp) -} diff --git a/main/commands/all/api/inbounds_add.go b/main/commands/all/api/inbounds_add.go index 9bad6f87..dad8b0f9 100644 --- a/main/commands/all/api/inbounds_add.go +++ b/main/commands/all/api/inbounds_add.go @@ -15,18 +15,13 @@ var cmdAddInbounds = &base.Command{ Short: "Add inbounds", Long: ` Add inbounds to Xray. - Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - + -t, -timeout + Timeout seconds to call API. Default 3 Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json + {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json `, Run: executeAddInbounds, } diff --git a/main/commands/all/api/inbounds_list.go b/main/commands/all/api/inbounds_list.go deleted file mode 100644 index 6060074c..00000000 --- a/main/commands/all/api/inbounds_list.go +++ /dev/null @@ -1,47 +0,0 @@ -package api - -import ( - handlerService "github.com/xtls/xray-core/app/proxyman/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdListInbounds = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api lsi [--server=127.0.0.1:8080] [--isOnlyTags=true]", - Short: "List inbounds", - Long: ` -List inbounds in Xray. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -`, - Run: executeListInbounds, -} - -func executeListInbounds(cmd *base.Command, args []string) { - setSharedFlags(cmd) - var isOnlyTagsStr string - cmd.Flag.StringVar(&isOnlyTagsStr, "isOnlyTags", "", "") - cmd.Flag.Parse(args) - isOnlyTags := isOnlyTagsStr == "true" - - conn, ctx, close := dialAPIServer() - defer close() - - client := handlerService.NewHandlerServiceClient(conn) - - resp, err := client.ListInbounds(ctx, &handlerService.ListInboundsRequest{IsOnlyTags: isOnlyTags}) - if err != nil { - base.Fatalf("failed to list inbounds: %s", err) - } - showJSONResponse(resp) -} diff --git a/main/commands/all/api/inbounds_remove.go b/main/commands/all/api/inbounds_remove.go index 400a239e..9ab83e20 100644 --- a/main/commands/all/api/inbounds_remove.go +++ b/main/commands/all/api/inbounds_remove.go @@ -14,18 +14,13 @@ var cmdRemoveInbounds = &base.Command{ Short: "Remove inbounds", Long: ` Remove inbounds from Xray. - Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - + -t, -timeout + Timeout seconds to call API. Default 3 Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name" + {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name" `, Run: executeRemoveInbounds, } diff --git a/main/commands/all/api/logger_restart.go b/main/commands/all/api/logger_restart.go index 15dacc15..7bd6f10d 100644 --- a/main/commands/all/api/logger_restart.go +++ b/main/commands/all/api/logger_restart.go @@ -11,18 +11,11 @@ var cmdRestartLogger = &base.Command{ Short: "Restart the logger", Long: ` Restart the logger of Xray. - Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 + -t, -timeout + Timeout seconds to call API. Default 3 `, Run: executeRestartLogger, } diff --git a/main/commands/all/api/outbounds_add.go b/main/commands/all/api/outbounds_add.go index 8d36f75a..5066e543 100644 --- a/main/commands/all/api/outbounds_add.go +++ b/main/commands/all/api/outbounds_add.go @@ -15,18 +15,13 @@ var cmdAddOutbounds = &base.Command{ Short: "Add outbounds", Long: ` Add outbounds to Xray. - Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - + -t, -timeout + Timeout seconds to call API. Default 3 Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json + {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json `, Run: executeAddOutbounds, } diff --git a/main/commands/all/api/outbounds_list.go b/main/commands/all/api/outbounds_list.go deleted file mode 100644 index be7f077b..00000000 --- a/main/commands/all/api/outbounds_list.go +++ /dev/null @@ -1,43 +0,0 @@ -package api - -import ( - handlerService "github.com/xtls/xray-core/app/proxyman/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdListOutbounds = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api lso [--server=127.0.0.1:8080]", - Short: "List outbounds", - Long: ` -List outbounds in Xray. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -`, - Run: executeListOutbounds, -} - -func executeListOutbounds(cmd *base.Command, args []string) { - setSharedFlags(cmd) - cmd.Flag.Parse(args) - - conn, ctx, close := dialAPIServer() - defer close() - - client := handlerService.NewHandlerServiceClient(conn) - resp, err := client.ListOutbounds(ctx, &handlerService.ListOutboundsRequest{}) - if err != nil { - base.Fatalf("failed to list outbounds: %s", err) - } - showJSONResponse(resp) -} diff --git a/main/commands/all/api/outbounds_remove.go b/main/commands/all/api/outbounds_remove.go index a081bc62..9fdbc078 100644 --- a/main/commands/all/api/outbounds_remove.go +++ b/main/commands/all/api/outbounds_remove.go @@ -14,18 +14,13 @@ var cmdRemoveOutbounds = &base.Command{ Short: "Remove outbounds", Long: ` Remove outbounds from Xray. - Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - + -t, -timeout + Timeout seconds to call API. Default 3 Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name" + {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name" `, Run: executeRemoveOutbounds, } diff --git a/main/commands/all/api/rules_add.go b/main/commands/all/api/rules_add.go deleted file mode 100644 index 2e1404e6..00000000 --- a/main/commands/all/api/rules_add.go +++ /dev/null @@ -1,93 +0,0 @@ -package api - -import ( - "fmt" - - routerService "github.com/xtls/xray-core/app/router/command" - cserial "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/infra/conf" - "github.com/xtls/xray-core/infra/conf/serial" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdAddRules = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api adrules [--server=127.0.0.1:8080] [c2.json]...", - Short: "Add routing rules", - Long: ` -Add routing rules to Xray. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout seconds to call API. Default 3 - - -append - Append to the existing configuration instead of replacing it. Default false - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json -`, - Run: executeAddRules, -} - -func executeAddRules(cmd *base.Command, args []string) { - var ( - shouldAppend bool - ) - setSharedFlags(cmd) - cmd.Flag.BoolVar(&shouldAppend, "append", false, "") - cmd.Flag.Parse(args) - - unnamedArgs := cmd.Flag.Args() - if len(unnamedArgs) == 0 { - fmt.Println("reading from stdin:") - unnamedArgs = []string{"stdin:"} - } - conn, ctx, close := dialAPIServer() - defer close() - - client := routerService.NewRoutingServiceClient(conn) - - rcs := make([]conf.RouterConfig, 0) - for _, arg := range unnamedArgs { - r, err := loadArg(arg) - if err != nil { - base.Fatalf("failed to load %s: %s", arg, err) - } - conf, err := serial.DecodeJSONConfig(r) - if err != nil { - base.Fatalf("failed to decode %s: %s", arg, err) - } - rcs = append(rcs, *conf.RouterConfig) - } - if len(rcs) == 0 { - base.Fatalf("no valid rule found in config") - } - for _, in := range rcs { - - config, err := in.Build() - if err != nil { - base.Fatalf("failed to build conf: %s", err) - } - tmsg := cserial.ToTypedMessage(config) - if tmsg == nil { - base.Fatalf("failed to format config to TypedMessage.") - } - - ra := &routerService.AddRuleRequest{ - Config: tmsg, - ShouldAppend: shouldAppend, - } - resp, err := client.AddRule(ctx, ra) - if err != nil { - base.Fatalf("failed to perform AddRule: %s", err) - } - showJSONResponse(resp) - } - -} diff --git a/main/commands/all/api/rules_remove.go b/main/commands/all/api/rules_remove.go deleted file mode 100644 index ac9a8d09..00000000 --- a/main/commands/all/api/rules_remove.go +++ /dev/null @@ -1,60 +0,0 @@ -package api - -import ( - "fmt" - - routerService "github.com/xtls/xray-core/app/router/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdRemoveRules = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api rmrules [--server=127.0.0.1:8080] [ruleTag]...", - Short: "Remove routing rules by ruleTag", - Long: ` -Remove routing rules by ruleTag from Xray. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 ruleTag1 ruleTag2 -`, - Run: executeRemoveRules, -} - -func executeRemoveRules(cmd *base.Command, args []string) { - setSharedFlags(cmd) - cmd.Flag.Parse(args) - ruleTags := cmd.Flag.Args() - if len(ruleTags) == 0 { - fmt.Println("reading from stdin:") - ruleTags = []string{"stdin:"} - } - conn, ctx, close := dialAPIServer() - defer close() - - client := routerService.NewRoutingServiceClient(conn) - - if len(ruleTags) == 0 { - base.Fatalf("no valid ruleTag input") - } - for _, tag := range ruleTags { - - rr := &routerService.RemoveRuleRequest{ - RuleTag: tag, - } - resp, err := client.RemoveRule(ctx, rr) - if err != nil { - base.Fatalf("failed to perform RemoveRule: %s", err) - } - showJSONResponse(resp) - } - -} diff --git a/main/commands/all/api/shared.go b/main/commands/all/api/shared.go index 79f4171a..d3aaf0e1 100644 --- a/main/commands/all/api/shared.go +++ b/main/commands/all/api/shared.go @@ -3,7 +3,6 @@ package api import ( "bytes" "context" - "errors" "fmt" "io" "net/http" @@ -13,12 +12,10 @@ import ( "strings" "time" - "google.golang.org/grpc/credentials/insecure" - "github.com/xtls/xray-core/common/buf" - creflect "github.com/xtls/xray-core/common/reflect" "github.com/xtls/xray-core/main/commands/base" "google.golang.org/grpc" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) @@ -27,7 +24,6 @@ type serviceHandler func(ctx context.Context, conn *grpc.ClientConn, cmd *base.C var ( apiServerAddrPtr string apiTimeout int - apiJSON bool ) func setSharedFlags(cmd *base.Command) { @@ -35,12 +31,11 @@ func setSharedFlags(cmd *base.Command) { cmd.Flag.StringVar(&apiServerAddrPtr, "server", "127.0.0.1:8080", "") cmd.Flag.IntVar(&apiTimeout, "t", 3, "") cmd.Flag.IntVar(&apiTimeout, "timeout", 3, "") - cmd.Flag.BoolVar(&apiJSON, "json", false, "") } func dialAPIServer() (conn *grpc.ClientConn, ctx context.Context, close func()) { ctx, cancel := context.WithTimeout(context.Background(), time.Duration(apiTimeout)*time.Second) - conn, err := grpc.DialContext(ctx, apiServerAddrPtr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) + conn, err := grpc.DialContext(ctx, apiServerAddrPtr, grpc.WithInsecure(), grpc.WithBlock()) if err != nil { base.Fatalf("failed to dial %s", apiServerAddrPtr) } @@ -102,22 +97,31 @@ func fetchHTTPContent(target string) ([]byte, error) { content, err := buf.ReadAllToBytes(resp.Body) if err != nil { - return nil, errors.New("failed to read HTTP response") + return nil, fmt.Errorf("failed to read HTTP response") } return content, nil } +func protoToJSONString(m proto.Message, _, indent string) (string, error) { + ops := protojson.MarshalOptions{ + Indent: indent, + EmitUnpopulated: true, + } + b, err := ops.Marshal(m) + return string(b), err +} + func showJSONResponse(m proto.Message) { if isNil(m) { return } - if j, ok := creflect.MarshalToJson(m, true); ok { - fmt.Println(j) - } else { + output, err := protoToJSONString(m, "", " ") + if err != nil { fmt.Fprintf(os.Stdout, "%v\n", m) - base.Fatalf("error encode json") + base.Fatalf("error encode json: %s", err) } + fmt.Println(output) } func isNil(i interface{}) bool { diff --git a/main/commands/all/api/source_ip_block.go b/main/commands/all/api/source_ip_block.go deleted file mode 100644 index 11f3d55d..00000000 --- a/main/commands/all/api/source_ip_block.go +++ /dev/null @@ -1,141 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "strings" - - routerService "github.com/xtls/xray-core/app/router/command" - cserial "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/infra/conf/serial" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdSourceIpBlock = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api sib [--server=127.0.0.1:8080] -outbound=blocked -inbound=socks 1.2.3.4", - Short: "Block connections by source IP", - Long: ` -Block connections by source IP address. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - - -outbound - Specifies the outbound tag. - - -inbound - Specifies the inbound tag. - - -ruletag - The ruleTag. Default sourceIpBlock - - -reset - remove ruletag and apply new source IPs. Default false - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -outbound=blocked -inbound=socks 1.2.3.4 - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -outbound=blocked -inbound=socks 1.2.3.4 -reset -`, - Run: executeSourceIpBlock, -} - -func executeSourceIpBlock(cmd *base.Command, args []string) { - var ( - inbound string - outbound string - ruletag string - reset bool - ) - setSharedFlags(cmd) - cmd.Flag.StringVar(&inbound, "inbound", "", "") - cmd.Flag.StringVar(&outbound, "outbound", "", "") - cmd.Flag.StringVar(&ruletag, "ruletag", "sourceIpBlock", "") - cmd.Flag.BoolVar(&reset, "reset", false, "") - - cmd.Flag.Parse(args) - - unnamedArgs := cmd.Flag.Args() - if len(unnamedArgs) == 0 { - fmt.Println("reading from stdin:") - unnamedArgs = []string{"stdin:"} - } - conn, ctx, close := dialAPIServer() - defer close() - - client := routerService.NewRoutingServiceClient(conn) - - jsonIps, err := json.Marshal(unnamedArgs) - if err != nil { - fmt.Println("Error marshaling JSON:", err) - return - } - - jsonInbound, err := json.Marshal([]string{inbound}) - if inbound == "" { - jsonInbound, err = json.Marshal([]string{}) - } - if err != nil { - fmt.Println("Error marshaling JSON:", err) - return - } - stringConfig := fmt.Sprintf(` - { - "routing": { - "rules": [ - { - "ruleTag" : "%s", - "inboundTag": %s, - "outboundTag": "%s", - "type": "field", - "source": %s - } - ] - } - } - - `, ruletag, string(jsonInbound), outbound, string(jsonIps)) - - conf, err := serial.DecodeJSONConfig(strings.NewReader(stringConfig)) - if err != nil { - base.Fatalf("failed to decode : %s", err) - } - rc := *conf.RouterConfig - - config, err := rc.Build() - if err != nil { - base.Fatalf("failed to build conf: %s", err) - } - tmsg := cserial.ToTypedMessage(config) - if tmsg == nil { - base.Fatalf("failed to format config to TypedMessage.") - } - - if reset { - rr := &routerService.RemoveRuleRequest{ - RuleTag: ruletag, - } - resp, err := client.RemoveRule(ctx, rr) - if err != nil { - base.Fatalf("failed to perform RemoveRule: %s", err) - } - showJSONResponse(resp) - - } - ra := &routerService.AddRuleRequest{ - Config: tmsg, - ShouldAppend: true, - } - resp, err := client.AddRule(ctx, ra) - if err != nil { - base.Fatalf("failed to perform AddRule: %s", err) - } - showJSONResponse(resp) - -} diff --git a/main/commands/all/api/stats_get.go b/main/commands/all/api/stats_get.go index 9b5d82f9..c03fe5f8 100644 --- a/main/commands/all/api/stats_get.go +++ b/main/commands/all/api/stats_get.go @@ -8,26 +8,19 @@ import ( var cmdGetStats = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api stats [--server=127.0.0.1:8080] [-name '']", - Short: "Retrieve statistics", + Short: "Get statistics", Long: ` -Retrieve the statistics from Xray. - +Get statistics from Xray. Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - + -t, -timeout + Timeout seconds to call API. Default 3 -name - Name of the counter. - + Name of the stat counter. -reset - Reset the counter after fetching their values. Default false - + Reset the counter to fetching its value. Example: - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -name "inbound>>>statin>>>traffic>>>downlink" `, Run: executeGetStats, diff --git a/main/commands/all/api/stats_online.go b/main/commands/all/api/stats_online.go deleted file mode 100644 index aca547a6..00000000 --- a/main/commands/all/api/stats_online.go +++ /dev/null @@ -1,51 +0,0 @@ -package api - -import ( - statsService "github.com/xtls/xray-core/app/stats/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdOnlineStats = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api statsonline [--server=127.0.0.1:8080] [-email '']", - Short: "Retrieve the online session count for a user", - Long: ` -Retrieve the current number of active sessions for a user from Xray. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - - -email - The user's email address. - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -email "xray@love.com" -`, - Run: executeOnlineStats, -} - -func executeOnlineStats(cmd *base.Command, args []string) { - setSharedFlags(cmd) - email := cmd.Flag.String("email", "", "") - cmd.Flag.Parse(args) - statName := "user>>>" + *email + ">>>online" - conn, ctx, close := dialAPIServer() - defer close() - - client := statsService.NewStatsServiceClient(conn) - r := &statsService.GetStatsRequest{ - Name: statName, - Reset_: false, - } - resp, err := client.GetStatsOnline(ctx, r) - if err != nil { - base.Fatalf("failed to get stats: %s", err) - } - showJSONResponse(resp) -} diff --git a/main/commands/all/api/stats_online_ip_list.go b/main/commands/all/api/stats_online_ip_list.go deleted file mode 100644 index 74e066f9..00000000 --- a/main/commands/all/api/stats_online_ip_list.go +++ /dev/null @@ -1,51 +0,0 @@ -package api - -import ( - statsService "github.com/xtls/xray-core/app/stats/command" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdOnlineStatsIpList = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} api statsonlineiplist [--server=127.0.0.1:8080] [-email '']", - Short: "Retrieve a user's online IP addresses and access times", - Long: ` -Retrieve the online IP addresses and corresponding access timestamps for a user from Xray. - -Arguments: - - -s, -server - The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - - -email - The user's email address. - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -email "xray@love.com" -`, - Run: executeOnlineStatsIpList, -} - -func executeOnlineStatsIpList(cmd *base.Command, args []string) { - setSharedFlags(cmd) - email := cmd.Flag.String("email", "", "") - cmd.Flag.Parse(args) - statName := "user>>>" + *email + ">>>online" - conn, ctx, close := dialAPIServer() - defer close() - - client := statsService.NewStatsServiceClient(conn) - r := &statsService.GetStatsRequest{ - Name: statName, - Reset_: false, - } - resp, err := client.GetStatsOnlineIpList(ctx, r) - if err != nil { - base.Fatalf("failed to get stats: %s", err) - } - showJSONResponse(resp) -} diff --git a/main/commands/all/api/stats_query.go b/main/commands/all/api/stats_query.go index 99d64437..f3163199 100644 --- a/main/commands/all/api/stats_query.go +++ b/main/commands/all/api/stats_query.go @@ -11,23 +11,16 @@ var cmdQueryStats = &base.Command{ Short: "Query statistics", Long: ` Query statistics from Xray. - Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - + -t, -timeout + Timeout seconds to call API. Default 3 -pattern - Filter pattern for the statistics query. - + Pattern of the query. -reset - Reset the counter after fetching their values. Default false - + Reset the counter to fetching its value. Example: - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -pattern "counter_" `, Run: executeQueryStats, diff --git a/main/commands/all/api/stats_sys.go b/main/commands/all/api/stats_sys.go index e34d056e..de7a8ce2 100644 --- a/main/commands/all/api/stats_sys.go +++ b/main/commands/all/api/stats_sys.go @@ -8,21 +8,14 @@ import ( var cmdSysStats = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api statssys [--server=127.0.0.1:8080]", - Short: "Retrieve system statistics", + Short: "Get system statistics", Long: ` -Retrieve system statistics from Xray. - +Get system statistics from Xray. Arguments: - - -s, -server + -s, -server The API server address. Default 127.0.0.1:8080 - - -t, -timeout - Timeout in seconds for calling API. Default 3 - -Example: - - {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 + -t, -timeout + Timeout seconds to call API. Default 3 `, Run: executeSysStats, } diff --git a/main/commands/all/commands.go b/main/commands/all/commands.go index 70457a72..9b8b49e0 100644 --- a/main/commands/all/commands.go +++ b/main/commands/all/commands.go @@ -2,19 +2,19 @@ package all import ( "github.com/xtls/xray-core/main/commands/all/api" - "github.com/xtls/xray-core/main/commands/all/convert" "github.com/xtls/xray-core/main/commands/all/tls" "github.com/xtls/xray-core/main/commands/base" ) +// go:generate go run github.com/xtls/xray-core/common/errors/errorgen + func init() { base.RootCommand.Commands = append( base.RootCommand.Commands, api.CmdAPI, - convert.CmdConvert, + // cmdConvert, tls.CmdTLS, cmdUUID, cmdX25519, - cmdWG, ) } diff --git a/main/commands/all/convert.go b/main/commands/all/convert.go new file mode 100644 index 00000000..2ed25a13 --- /dev/null +++ b/main/commands/all/convert.go @@ -0,0 +1,125 @@ +package all + +import ( + "bytes" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strings" + "time" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/infra/conf" + "github.com/xtls/xray-core/infra/conf/serial" + "github.com/xtls/xray-core/main/commands/base" + "google.golang.org/protobuf/proto" +) + +var cmdConvert = &base.Command{ + UsageLine: "{{.Exec}} convert [json file] [json file] ...", + Short: "Convert multiple json config to protobuf", + Long: ` +Convert multiple json config to protobuf. + +Examples: + + {{.Exec}} convert config.json c1.json c2.json .json + `, +} + +func init() { + cmdConvert.Run = executeConvert // break init loop +} + +func executeConvert(cmd *base.Command, args []string) { + unnamedArgs := cmdConvert.Flag.Args() + if len(unnamedArgs) < 1 { + base.Fatalf("empty config list") + } + + conf := &conf.Config{} + for _, arg := range unnamedArgs { + fmt.Fprintf(os.Stderr, "Read config: %s", arg) + r, err := loadArg(arg) + common.Must(err) + c, err := serial.DecodeJSONConfig(r) + if err != nil { + base.Fatalf(err.Error()) + } + conf.Override(c, arg) + } + + pbConfig, err := conf.Build() + if err != nil { + base.Fatalf(err.Error()) + } + + bytesConfig, err := proto.Marshal(pbConfig) + if err != nil { + base.Fatalf("failed to marshal proto config: %s", err) + } + + if _, err := os.Stdout.Write(bytesConfig); err != nil { + base.Fatalf("failed to write proto config: %s", err) + } +} + +// loadArg loads one arg, maybe an remote url, or local file path +func loadArg(arg string) (out io.Reader, err error) { + var data []byte + switch { + case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"): + data, err = FetchHTTPContent(arg) + + case arg == "stdin:": + data, err = io.ReadAll(os.Stdin) + + default: + data, err = os.ReadFile(arg) + } + + if err != nil { + return + } + out = bytes.NewBuffer(data) + return +} + +// FetchHTTPContent dials https for remote content +func FetchHTTPContent(target string) ([]byte, error) { + parsedTarget, err := url.Parse(target) + if err != nil { + return nil, newError("invalid URL: ", target).Base(err) + } + + if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" { + return nil, newError("invalid scheme: ", parsedTarget.Scheme) + } + + client := &http.Client{ + Timeout: 30 * time.Second, + } + resp, err := client.Do(&http.Request{ + Method: "GET", + URL: parsedTarget, + Close: true, + }) + if err != nil { + return nil, newError("failed to dial to ", target).Base(err) + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return nil, newError("unexpected HTTP status code: ", resp.StatusCode) + } + + content, err := buf.ReadAllToBytes(resp.Body) + if err != nil { + return nil, newError("failed to read HTTP response").Base(err) + } + + return content, nil +} diff --git a/main/commands/all/convert/convert.go b/main/commands/all/convert/convert.go deleted file mode 100644 index 3543b2e9..00000000 --- a/main/commands/all/convert/convert.go +++ /dev/null @@ -1,17 +0,0 @@ -package convert - -import ( - "github.com/xtls/xray-core/main/commands/base" -) - -// CmdConvert do config convertion -var CmdConvert = &base.Command{ - UsageLine: "{{.Exec}} convert", - Short: "Convert configs", - Long: `{{.Exec}} {{.LongName}} provides tools to convert config. -`, - Commands: []*base.Command{ - cmdProtobuf, - cmdJson, - }, -} diff --git a/main/commands/all/convert/json.go b/main/commands/all/convert/json.go deleted file mode 100644 index 58066795..00000000 --- a/main/commands/all/convert/json.go +++ /dev/null @@ -1,71 +0,0 @@ -package convert - -import ( - "encoding/json" - "fmt" - "io" - - creflect "github.com/xtls/xray-core/common/reflect" - cserial "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/main/commands/base" - "github.com/xtls/xray-core/main/confloader" -) - -var cmdJson = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} convert json [-type] [stdin:] [typedMessage file] ", - Short: "Convert typedMessage to json", - Long: ` -Convert ONE typedMessage to json. - -Where typedMessage file need to be in the following format: - -{ - "type": "xray.proxy.shadowsocks.Account", - "value": "CgMxMTEQBg==" -} - -Arguments: - - -t, -type - Inject type infomation. - -Examples: - - {{.Exec}} convert json user.tmsg - `, - Run: executeTypedMessageToJson, -} - -func executeTypedMessageToJson(cmd *base.Command, args []string) { - - var injectTypeInfo bool - cmd.Flag.BoolVar(&injectTypeInfo, "t", false, "") - cmd.Flag.BoolVar(&injectTypeInfo, "type", false, "") - cmd.Flag.Parse(args) - - if cmd.Flag.NArg() < 1 { - base.Fatalf("empty input list") - } - - reader, err := confloader.LoadConfig(cmd.Flag.Arg(0)) - if err != nil { - base.Fatalf("failed to load config: %s", err) - } - - b, err := io.ReadAll(reader) - if err != nil { - base.Fatalf("failed to read config: %s", err) - } - - tm := cserial.TypedMessage{} - if err = json.Unmarshal(b, &tm); err != nil { - base.Fatalf("failed to unmarshal config: %s", err) - } - - if j, ok := creflect.MarshalToJson(&tm, injectTypeInfo); ok { - fmt.Println(j) - } else { - base.Fatalf("marshal TypedMessage to json failed") - } -} diff --git a/main/commands/all/convert/protobuf.go b/main/commands/all/convert/protobuf.go deleted file mode 100644 index c77df963..00000000 --- a/main/commands/all/convert/protobuf.go +++ /dev/null @@ -1,81 +0,0 @@ -package convert - -import ( - "fmt" - "os" - - "github.com/xtls/xray-core/common/cmdarg" - creflect "github.com/xtls/xray-core/common/reflect" - "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/main/commands/base" - - "google.golang.org/protobuf/proto" -) - -var cmdProtobuf = &base.Command{ - CustomFlags: true, - UsageLine: "{{.Exec}} convert pb [-debug] [-type] [json file] [json file] ...", - Short: "Convert multiple json configs to protobuf", - Long: ` -Convert multiple json configs to protobuf. - -Arguments: - - -d, -debug - Show mix.pb as json. - FOR DEBUGGING ONLY! - DO NOT PASS THIS OUTPUT TO XRAY-CORE! - - -t, -type - Inject type information into debug output. - -Examples: - - {{.Exec}} convert pb config.json c1.json c2.json c3.json > mix.pb - `, - Run: executeConvertConfigsToProtobuf, -} - -func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) { - - var optDump bool - var optType bool - - cmd.Flag.BoolVar(&optDump, "d", false, "") - cmd.Flag.BoolVar(&optDump, "debug", false, "") - cmd.Flag.BoolVar(&optType, "t", false, "") - cmd.Flag.BoolVar(&optType, "type", false, "") - cmd.Flag.Parse(args) - - unnamedArgs := cmdarg.Arg{} - for _, v := range cmd.Flag.Args() { - unnamedArgs.Set(v) - } - - if len(unnamedArgs) < 1 { - base.Fatalf("invalid config list length: %d", len(unnamedArgs)) - } - - pbConfig, err := core.LoadConfig("auto", unnamedArgs) - if err != nil { - base.Fatalf("failed to load config: %s", err) - } - - if optDump { - if j, ok := creflect.MarshalToJson(pbConfig, optType); ok { - fmt.Println(j) - return - } else { - base.Fatalf("failed to marshal proto config to json.") - } - } - - bytesConfig, err := proto.Marshal(pbConfig) - if err != nil { - base.Fatalf("failed to marshal proto config: %s", err) - } - - if _, err := os.Stdout.Write(bytesConfig); err != nil { - base.Fatalf("failed to write proto config: %s", err) - } -} diff --git a/main/commands/all/curve25519.go b/main/commands/all/curve25519.go deleted file mode 100644 index bb706c6c..00000000 --- a/main/commands/all/curve25519.go +++ /dev/null @@ -1,58 +0,0 @@ -package all - -import ( - "crypto/rand" - "encoding/base64" - "fmt" - - "golang.org/x/crypto/curve25519" -) - -func Curve25519Genkey(StdEncoding bool, input_base64 string) { - var output string - var err error - var privateKey, publicKey []byte - var encoding *base64.Encoding - if *input_stdEncoding || StdEncoding { - encoding = base64.StdEncoding - } else { - encoding = base64.RawURLEncoding - } - - if len(input_base64) > 0 { - privateKey, err = encoding.DecodeString(input_base64) - if err != nil { - output = err.Error() - goto out - } - if len(privateKey) != curve25519.ScalarSize { - output = "Invalid length of private key." - goto out - } - } - - if privateKey == nil { - privateKey = make([]byte, curve25519.ScalarSize) - if _, err = rand.Read(privateKey); err != nil { - output = err.Error() - goto out - } - } - - // Modify random bytes using algorithm described at: - // https://cr.yp.to/ecdh.html. - privateKey[0] &= 248 - privateKey[31] &= 127 - privateKey[31] |= 64 - - if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil { - output = err.Error() - goto out - } - - output = fmt.Sprintf("Private key: %v\nPublic key: %v", - encoding.EncodeToString(privateKey), - encoding.EncodeToString(publicKey)) -out: - fmt.Println(output) -} diff --git a/main/commands/all/errors.generated.go b/main/commands/all/errors.generated.go new file mode 100644 index 00000000..ff138a8a --- /dev/null +++ b/main/commands/all/errors.generated.go @@ -0,0 +1,9 @@ +package all + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/main/commands/all/tls/cert.go b/main/commands/all/tls/cert.go index 83cb42b4..c7e39eb6 100644 --- a/main/commands/all/tls/cert.go +++ b/main/commands/all/tls/cert.go @@ -120,9 +120,9 @@ func writeFile(content []byte, name string) error { func printFile(certificate *cert.Certificate, name string) error { certPEM, keyPEM := certificate.ToPEM() return task.Run(context.Background(), func() error { - return writeFile(certPEM, name+".crt") + return writeFile(certPEM, name+"_cert.pem") }, func() error { - return writeFile(keyPEM, name+".key") + return writeFile(keyPEM, name+"_key.pem") }) } diff --git a/main/commands/all/tls/certchainhash.go b/main/commands/all/tls/certchainhash.go index 304f668d..3fbdb4c4 100644 --- a/main/commands/all/tls/certchainhash.go +++ b/main/commands/all/tls/certchainhash.go @@ -3,7 +3,7 @@ package tls import ( "flag" "fmt" - "os" + "io/ioutil" "github.com/xtls/xray-core/main/commands/base" "github.com/xtls/xray-core/transport/internet/tls" @@ -30,11 +30,12 @@ func executeCertChainHash(cmd *base.Command, args []string) { fmt.Println(err) return } - certContent, err := os.ReadFile(*input) + certContent, err := ioutil.ReadFile(*input) if err != nil { fmt.Println(err) return } certChainHashB64 := tls.CalculatePEMCertChainSHA256Hash(certContent) fmt.Println(certChainHashB64) + return } diff --git a/main/commands/all/tls/ech.go b/main/commands/all/tls/ech.go deleted file mode 100644 index d4e17f9b..00000000 --- a/main/commands/all/tls/ech.go +++ /dev/null @@ -1,69 +0,0 @@ -package tls - -import ( - "encoding/json" - "encoding/pem" - "os" - "strings" - - "github.com/OmarTariq612/goech" - "github.com/cloudflare/circl/hpke" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdECH = &base.Command{ - UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--json]`, - Short: `Generate TLS-ECH certificates`, - Long: ` -Generate TLS-ECH certificates. - -Set serverName to your custom string: {{.Exec}} tls ech --serverName (string) -Generate into json format: {{.Exec}} tls ech --json -`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled -} - -func init() { - cmdECH.Run = executeECH -} - -var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "") -var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "") -var input_json = cmdECH.Flag.Bool("json", false, "True == turn on json output") - -func executeECH(cmd *base.Command, args []string) { - var kem hpke.KEM - - if *input_pqSignatureSchemesEnabled { - kem = hpke.KEM_X25519_KYBER768_DRAFT00 - } else { - kem = hpke.KEM_X25519_HKDF_SHA256 - } - - echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem) - common.Must(err) - - configBuffer, _ := echKeySet.ECHConfig.MarshalBinary() - keyBuffer, _ := echKeySet.MarshalBinary() - - configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer})) - keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer})) - if *input_json { - jECHConfigs := map[string]interface{}{ - "configs": strings.Split(strings.TrimSpace(string(configPEM)), "\n"), - } - jECHKey := map[string]interface{}{ - "key": strings.Split(strings.TrimSpace(string(keyPEM)), "\n"), - } - - for _, i := range []map[string]interface{}{jECHConfigs, jECHKey} { - content, err := json.MarshalIndent(i, "", " ") - common.Must(err) - os.Stdout.Write(content) - os.Stdout.WriteString("\n") - } - } else { - os.Stdout.WriteString(configPEM) - os.Stdout.WriteString(keyPEM) - } -} diff --git a/main/commands/all/tls/tls.go b/main/commands/all/tls/tls.go index a93da1c3..bf653301 100644 --- a/main/commands/all/tls/tls.go +++ b/main/commands/all/tls/tls.go @@ -14,6 +14,5 @@ var CmdTLS = &base.Command{ cmdCert, cmdPing, cmdCertChainHash, - cmdECH, }, } diff --git a/main/commands/all/wg.go b/main/commands/all/wg.go deleted file mode 100644 index 70da4668..00000000 --- a/main/commands/all/wg.go +++ /dev/null @@ -1,27 +0,0 @@ -package all - -import ( - "github.com/xtls/xray-core/main/commands/base" -) - -var cmdWG = &base.Command{ - UsageLine: `{{.Exec}} wg [-i "private key (base64.StdEncoding)"]`, - Short: `Generate key pair for wireguard key exchange`, - Long: ` -Generate key pair for wireguard key exchange. - -Random: {{.Exec}} wg - -From private key: {{.Exec}} wg -i "private key (base64.StdEncoding)" -`, -} - -func init() { - cmdWG.Run = executeWG // break init loop -} - -var input_wireguard = cmdWG.Flag.String("i", "", "") - -func executeWG(cmd *base.Command, args []string) { - Curve25519Genkey(true, *input_wireguard) -} diff --git a/main/commands/all/x25519.go b/main/commands/all/x25519.go index 73f669b2..e7909d9b 100644 --- a/main/commands/all/x25519.go +++ b/main/commands/all/x25519.go @@ -1,11 +1,16 @@ package all import ( + "crypto/rand" + "encoding/base64" + "fmt" + "github.com/xtls/xray-core/main/commands/base" + "golang.org/x/crypto/curve25519" ) var cmdX25519 = &base.Command{ - UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`, + UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"]`, Short: `Generate key pair for x25519 key exchange`, Long: ` Generate key pair for x25519 key exchange. @@ -13,7 +18,6 @@ Generate key pair for x25519 key exchange. Random: {{.Exec}} x25519 From private key: {{.Exec}} x25519 -i "private key (base64.RawURLEncoding)" -For Std Encoding: {{.Exec}} x25519 --std-encoding `, } @@ -21,9 +25,47 @@ func init() { cmdX25519.Run = executeX25519 // break init loop } -var input_stdEncoding = cmdX25519.Flag.Bool("std-encoding", false, "") -var input_x25519 = cmdX25519.Flag.String("i", "", "") +var input_base64 = cmdX25519.Flag.String("i", "", "") func executeX25519(cmd *base.Command, args []string) { - Curve25519Genkey(false, *input_x25519) + var output string + var err error + var privateKey []byte + var publicKey []byte + if len(*input_base64) > 0 { + privateKey, err = base64.RawURLEncoding.DecodeString(*input_base64) + if err != nil { + output = err.Error() + goto out + } + if len(privateKey) != curve25519.ScalarSize { + output = "Invalid length of private key." + goto out + } + } + + if privateKey == nil { + privateKey = make([]byte, curve25519.ScalarSize) + if _, err = rand.Read(privateKey); err != nil { + output = err.Error() + goto out + } + } + + // Modify random bytes using algorithm described at: + // https://cr.yp.to/ecdh.html. + privateKey[0] &= 248 + privateKey[31] &= 127 + privateKey[31] |= 64 + + if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil { + output = err.Error() + goto out + } + + output = fmt.Sprintf("Private key: %v\nPublic key: %v", + base64.RawURLEncoding.EncodeToString(privateKey), + base64.RawURLEncoding.EncodeToString(publicKey)) +out: + fmt.Println(output) } diff --git a/main/commands/base/env.go b/main/commands/base/env.go index 27ba0b1c..476ed133 100644 --- a/main/commands/base/env.go +++ b/main/commands/base/env.go @@ -1,8 +1,13 @@ package base +import ( + "os" + "path" +) + // CommandEnvHolder is a struct holds the environment info of commands type CommandEnvHolder struct { - // Executable name of current binary + // Excutable name of current binary Exec string // commands column width of current command CommandsWidth int @@ -12,12 +17,10 @@ type CommandEnvHolder struct { var CommandEnv CommandEnvHolder func init() { - /* - exec, err := os.Executable() - if err != nil { - return - } - CommandEnv.Exec = path.Base(exec) - */ + exec, err := os.Executable() + if err != nil { + return + } + CommandEnv.Exec = path.Base(exec) CommandEnv.Exec = "xray" } diff --git a/main/commands/base/execute.go b/main/commands/base/execute.go index 8e23dabf..2d18371e 100644 --- a/main/commands/base/execute.go +++ b/main/commands/base/execute.go @@ -12,7 +12,7 @@ import ( // Use of this source code is governed by a BSD-style // copied from "github.com/golang/go/main.go" -// Execute execute the commands +// Execute excute the commands func Execute() { flag.Parse() args := flag.Args() diff --git a/main/confloader/confloader.go b/main/confloader/confloader.go index 315c500e..80b3b306 100644 --- a/main/confloader/confloader.go +++ b/main/confloader/confloader.go @@ -1,11 +1,8 @@ package confloader import ( - "context" "io" "os" - - "github.com/xtls/xray-core/common/errors" ) type ( @@ -22,7 +19,7 @@ var ( // actual work is in external module func LoadConfig(file string) (io.Reader, error) { if EffectiveConfigFileLoader == nil { - errors.LogInfo(context.Background(), "external config module not loaded, reading from stdin") + newError("external config module not loaded, reading from stdin").AtInfo().WriteToLog() return os.Stdin, nil } return EffectiveConfigFileLoader(file) @@ -32,7 +29,7 @@ func LoadConfig(file string) (io.Reader, error) { // the actual work also in external module func LoadExtConfig(files []string, reader io.Reader) (io.Reader, error) { if EffectiveExtConfigLoader == nil { - return nil, errors.New("external config module not loaded").AtError() + return nil, newError("external config module not loaded").AtError() } return EffectiveExtConfigLoader(files, reader) diff --git a/main/confloader/errors.generated.go b/main/confloader/errors.generated.go new file mode 100644 index 00000000..1c234856 --- /dev/null +++ b/main/confloader/errors.generated.go @@ -0,0 +1,9 @@ +package confloader + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/main/confloader/external/errors.generated.go b/main/confloader/external/errors.generated.go new file mode 100644 index 00000000..992674b7 --- /dev/null +++ b/main/confloader/external/errors.generated.go @@ -0,0 +1,9 @@ +package external + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/main/confloader/external/external.go b/main/confloader/external/external.go index f1ad437b..20afa141 100644 --- a/main/confloader/external/external.go +++ b/main/confloader/external/external.go @@ -1,5 +1,7 @@ package external +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "bytes" "io" @@ -10,7 +12,6 @@ import ( "time" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/platform/ctlcmd" "github.com/xtls/xray-core/main/confloader" ) @@ -38,11 +39,11 @@ func ConfigLoader(arg string) (out io.Reader, err error) { func FetchHTTPContent(target string) ([]byte, error) { parsedTarget, err := url.Parse(target) if err != nil { - return nil, errors.New("invalid URL: ", target).Base(err) + return nil, newError("invalid URL: ", target).Base(err) } if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" { - return nil, errors.New("invalid scheme: ", parsedTarget.Scheme) + return nil, newError("invalid scheme: ", parsedTarget.Scheme) } client := &http.Client{ @@ -54,17 +55,17 @@ func FetchHTTPContent(target string) ([]byte, error) { Close: true, }) if err != nil { - return nil, errors.New("failed to dial to ", target).Base(err) + return nil, newError("failed to dial to ", target).Base(err) } defer resp.Body.Close() if resp.StatusCode != 200 { - return nil, errors.New("unexpected HTTP status code: ", resp.StatusCode) + return nil, newError("unexpected HTTP status code: ", resp.StatusCode) } content, err := buf.ReadAllToBytes(resp.Body) if err != nil { - return nil, errors.New("failed to read HTTP response").Base(err) + return nil, newError("failed to read HTTP response").Base(err) } return content, nil diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 198abb3f..0e38fcf6 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -40,6 +40,7 @@ import ( _ "github.com/xtls/xray-core/proxy/freedom" _ "github.com/xtls/xray-core/proxy/http" _ "github.com/xtls/xray-core/proxy/loopback" + _ "github.com/xtls/xray-core/proxy/mtproto" _ "github.com/xtls/xray-core/proxy/shadowsocks" _ "github.com/xtls/xray-core/proxy/socks" _ "github.com/xtls/xray-core/proxy/trojan" @@ -50,11 +51,12 @@ import ( _ "github.com/xtls/xray-core/proxy/wireguard" // Transports + _ "github.com/xtls/xray-core/transport/internet/domainsocket" _ "github.com/xtls/xray-core/transport/internet/grpc" - _ "github.com/xtls/xray-core/transport/internet/httpupgrade" + _ "github.com/xtls/xray-core/transport/internet/http" _ "github.com/xtls/xray-core/transport/internet/kcp" + _ "github.com/xtls/xray-core/transport/internet/quic" _ "github.com/xtls/xray-core/transport/internet/reality" - _ "github.com/xtls/xray-core/transport/internet/splithttp" _ "github.com/xtls/xray-core/transport/internet/tcp" _ "github.com/xtls/xray-core/transport/internet/tls" _ "github.com/xtls/xray-core/transport/internet/udp" diff --git a/main/errors.generated.go b/main/errors.generated.go new file mode 100644 index 00000000..e898cb5f --- /dev/null +++ b/main/errors.generated.go @@ -0,0 +1,9 @@ +package main + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/main/json/errors.generated.go b/main/json/errors.generated.go new file mode 100644 index 00000000..2c1b1452 --- /dev/null +++ b/main/json/errors.generated.go @@ -0,0 +1,9 @@ +package json + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/main/json/json.go b/main/json/json.go index aad2dc6b..2e4168c3 100644 --- a/main/json/json.go +++ b/main/json/json.go @@ -1,12 +1,10 @@ package json import ( - "context" "io" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/cmdarg" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/infra/conf" "github.com/xtls/xray-core/infra/conf/serial" @@ -22,14 +20,14 @@ func init() { case cmdarg.Arg: cf := &conf.Config{} for i, arg := range v { - errors.LogInfo(context.Background(), "Reading config: ", arg) + newError("Reading config: ", arg).AtInfo().WriteToLog() r, err := confloader.LoadConfig(arg) if err != nil { - return nil, errors.New("failed to read config: ", arg).Base(err) + return nil, newError("failed to read config: ", arg).Base(err) } c, err := serial.DecodeJSONConfig(r) if err != nil { - return nil, errors.New("failed to decode config: ", arg).Base(err) + return nil, newError("failed to decode config: ", arg).Base(err) } if i == 0 { // This ensure even if the muti-json parser do not support a setting, @@ -43,7 +41,7 @@ func init() { case io.Reader: return serial.LoadJSONConfig(v) default: - return nil, errors.New("unknown type") + return nil, newError("unknow type") } }, })) diff --git a/main/run.go b/main/run.go index f74085d8..97967700 100644 --- a/main/run.go +++ b/main/run.go @@ -12,11 +12,8 @@ import ( "runtime/debug" "strings" "syscall" - "time" "github.com/xtls/xray-core/common/cmdarg" - "github.com/xtls/xray-core/common/errors" - clog "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/main/commands/base" @@ -37,21 +34,17 @@ The -format=json flag sets the format of config files. Default "auto". The -test flag tells Xray to test config files only, -without launching the server. - -The -dump flag tells Xray to print the merged config. +without launching the server `, } func init() { cmdRun.Run = executeRun // break init loop - log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) } var ( configFiles cmdarg.Arg // "Config file for Xray.", the option is customed type, parse in main configDir string - dump = cmdRun.Flag.Bool("dump", false, "Dump merged config only, without launching Xray server.") test = cmdRun.Flag.Bool("test", false, "Test config file only, without launching Xray server.") format = cmdRun.Flag.String("format", "auto", "Format of input file.") @@ -68,12 +61,6 @@ var ( ) func executeRun(cmd *base.Command, args []string) { - if *dump { - clog.ReplaceWithSeverityLogger(clog.Severity_Warning) - errCode := dumpConfig() - os.Exit(errCode) - } - printVersion() server, err := startXray() if err != nil { @@ -110,18 +97,6 @@ func executeRun(cmd *base.Command, args []string) { } } -func dumpConfig() int { - files := getConfigFilePath(false) - if config, err := core.GetMergedConfig(files); err != nil { - fmt.Println(err) - time.Sleep(1 * time.Second) - return 23 - } else { - fmt.Print(config) - } - return 0 -} - func fileExists(file string) bool { info, err := os.Stat(file) return err == nil && !info.IsDir() @@ -138,13 +113,13 @@ func dirExists(file string) bool { func getRegepxByFormat() string { switch strings.ToLower(*format) { case "json": - return `^.+\.(json|jsonc)$` + return `^.+\.json$` case "toml": return `^.+\.toml$` case "yaml", "yml": return `^.+\.(yaml|yml)$` default: - return `^.+\.(json|jsonc|toml|yaml|yml)$` + return `^.+\.(json|toml|yaml|yml)$` } } @@ -164,16 +139,12 @@ func readConfDir(dirPath string) { } } -func getConfigFilePath(verbose bool) cmdarg.Arg { +func getConfigFilePath() cmdarg.Arg { if dirExists(configDir) { - if verbose { - log.Println("Using confdir from arg:", configDir) - } + log.Println("Using confdir from arg:", configDir) readConfDir(configDir) } else if envConfDir := platform.GetConfDirPath(); dirExists(envConfDir) { - if verbose { - log.Println("Using confdir from env:", envConfDir) - } + log.Println("Using confdir from env:", envConfDir) readConfDir(envConfDir) } @@ -184,23 +155,17 @@ func getConfigFilePath(verbose bool) cmdarg.Arg { if workingDir, err := os.Getwd(); err == nil { configFile := filepath.Join(workingDir, "config.json") if fileExists(configFile) { - if verbose { - log.Println("Using default config: ", configFile) - } + log.Println("Using default config: ", configFile) return cmdarg.Arg{configFile} } } if configFile := platform.GetConfigurationPath(); fileExists(configFile) { - if verbose { - log.Println("Using config from env: ", configFile) - } + log.Println("Using config from env: ", configFile) return cmdarg.Arg{configFile} } - if verbose { - log.Println("Using config from STDIN") - } + log.Println("Using config from STDIN") return cmdarg.Arg{"stdin:"} } @@ -213,18 +178,18 @@ func getConfigFormat() string { } func startXray() (core.Server, error) { - configFiles := getConfigFilePath(true) + configFiles := getConfigFilePath() // config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles) c, err := core.LoadConfig(getConfigFormat(), configFiles) if err != nil { - return nil, errors.New("failed to load config files: [", configFiles.String(), "]").Base(err) + return nil, newError("failed to load config files: [", configFiles.String(), "]").Base(err) } server, err := core.New(c) if err != nil { - return nil, errors.New("failed to create server").Base(err) + return nil, newError("failed to create server").Base(err) } return server, nil diff --git a/main/toml/errors.generated.go b/main/toml/errors.generated.go new file mode 100644 index 00000000..0bad202e --- /dev/null +++ b/main/toml/errors.generated.go @@ -0,0 +1,9 @@ +package toml + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/main/toml/toml.go b/main/toml/toml.go index b17526ec..e52077f3 100644 --- a/main/toml/toml.go +++ b/main/toml/toml.go @@ -1,12 +1,10 @@ package toml import ( - "context" "io" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/cmdarg" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/infra/conf" "github.com/xtls/xray-core/infra/conf/serial" @@ -22,14 +20,14 @@ func init() { case cmdarg.Arg: cf := &conf.Config{} for i, arg := range v { - errors.LogInfo(context.Background(), "Reading config: ", arg) + newError("Reading config: ", arg).AtInfo().WriteToLog() r, err := confloader.LoadConfig(arg) if err != nil { - return nil, errors.New("failed to read config: ", arg).Base(err) + return nil, newError("failed to read config: ", arg).Base(err) } c, err := serial.DecodeTOMLConfig(r) if err != nil { - return nil, errors.New("failed to decode config: ", arg).Base(err) + return nil, newError("failed to decode config: ", arg).Base(err) } if i == 0 { // This ensure even if the muti-json parser do not support a setting, @@ -43,7 +41,7 @@ func init() { case io.Reader: return serial.LoadTOMLConfig(v) default: - return nil, errors.New("unknown type") + return nil, newError("unknow type") } }, })) diff --git a/main/yaml/errors.generated.go b/main/yaml/errors.generated.go new file mode 100644 index 00000000..c4da8fa9 --- /dev/null +++ b/main/yaml/errors.generated.go @@ -0,0 +1,9 @@ +package yaml + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/main/yaml/yaml.go b/main/yaml/yaml.go index a2ead8a7..760a5717 100644 --- a/main/yaml/yaml.go +++ b/main/yaml/yaml.go @@ -1,12 +1,10 @@ package yaml import ( - "context" "io" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/cmdarg" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/infra/conf" "github.com/xtls/xray-core/infra/conf/serial" @@ -22,14 +20,14 @@ func init() { case cmdarg.Arg: cf := &conf.Config{} for i, arg := range v { - errors.LogInfo(context.Background(), "Reading config: ", arg) + newError("Reading config: ", arg).AtInfo().WriteToLog() r, err := confloader.LoadConfig(arg) if err != nil { - return nil, errors.New("failed to read config: ", arg).Base(err) + return nil, newError("failed to read config: ", arg).Base(err) } c, err := serial.DecodeYAMLConfig(r) if err != nil { - return nil, errors.New("failed to decode config: ", arg).Base(err) + return nil, newError("failed to decode config: ", arg).Base(err) } if i == 0 { // This ensure even if the muti-json parser do not support a setting, @@ -43,7 +41,7 @@ func init() { case io.Reader: return serial.LoadYAMLConfig(v) default: - return nil, errors.New("unknown type") + return nil, newError("unknow type") } }, })) diff --git a/proxy/blackhole/blackhole.go b/proxy/blackhole/blackhole.go index 998666cb..b17c60c4 100644 --- a/proxy/blackhole/blackhole.go +++ b/proxy/blackhole/blackhole.go @@ -1,12 +1,13 @@ // Package blackhole is an outbound handler that blocks all connections. package blackhole +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "time" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" ) @@ -29,10 +30,6 @@ func New(ctx context.Context, config *Config) (*Handler, error) { // Process implements OutboundHandler.Dispatch(). func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - ob.Name = "blackhole" - nBytes := h.response.WriteTo(link.Writer) if nBytes > 0 { // Sleep a little here to make sure the response is sent to client. diff --git a/proxy/blackhole/blackhole_test.go b/proxy/blackhole/blackhole_test.go index 6a9cb8e8..8e487e0c 100644 --- a/proxy/blackhole/blackhole_test.go +++ b/proxy/blackhole/blackhole_test.go @@ -7,15 +7,13 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/proxy/blackhole" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/pipe" ) func TestBlackholeHTTPResponse(t *testing.T) { - ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{}}) - handler, err := blackhole.New(ctx, &blackhole.Config{ + handler, err := blackhole.New(context.Background(), &blackhole.Config{ Response: serial.ToTypedMessage(&blackhole.HTTPResponse{}), }) common.Must(err) @@ -34,7 +32,7 @@ func TestBlackholeHTTPResponse(t *testing.T) { Reader: reader, Writer: writer, } - common.Must(handler.Process(ctx, &link, nil)) + common.Must(handler.Process(context.Background(), &link, nil)) common.Must(rerr) if mb.IsEmpty() { t.Error("expect http response, but nothing") diff --git a/proxy/blackhole/config.go b/proxy/blackhole/config.go index e4a16684..b0f8e60c 100644 --- a/proxy/blackhole/config.go +++ b/proxy/blackhole/config.go @@ -17,7 +17,7 @@ Content-Length: 0 // ResponseConfig is the configuration for blackhole responses. type ResponseConfig interface { - // WriteTo writes a predefined response to the specified buffer. + // WriteTo writes predefined response to the give buffer. WriteTo(buf.Writer) int32 } diff --git a/proxy/blackhole/config.pb.go b/proxy/blackhole/config.pb.go index cc1c0640..54d2279e 100644 --- a/proxy/blackhole/config.pb.go +++ b/proxy/blackhole/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/blackhole/config.proto package blackhole @@ -29,9 +29,11 @@ type NoneResponse struct { func (x *NoneResponse) Reset() { *x = NoneResponse{} - mi := &file_proxy_blackhole_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_blackhole_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *NoneResponse) String() string { @@ -42,7 +44,7 @@ func (*NoneResponse) ProtoMessage() {} func (x *NoneResponse) ProtoReflect() protoreflect.Message { mi := &file_proxy_blackhole_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -65,9 +67,11 @@ type HTTPResponse struct { func (x *HTTPResponse) Reset() { *x = HTTPResponse{} - mi := &file_proxy_blackhole_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_blackhole_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *HTTPResponse) String() string { @@ -78,7 +82,7 @@ func (*HTTPResponse) ProtoMessage() {} func (x *HTTPResponse) ProtoReflect() protoreflect.Message { mi := &file_proxy_blackhole_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -103,9 +107,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_blackhole_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_blackhole_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -116,7 +122,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_blackhole_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -175,7 +181,7 @@ func file_proxy_blackhole_config_proto_rawDescGZIP() []byte { } var file_proxy_blackhole_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_proxy_blackhole_config_proto_goTypes = []any{ +var file_proxy_blackhole_config_proto_goTypes = []interface{}{ (*NoneResponse)(nil), // 0: xray.proxy.blackhole.NoneResponse (*HTTPResponse)(nil), // 1: xray.proxy.blackhole.HTTPResponse (*Config)(nil), // 2: xray.proxy.blackhole.Config @@ -195,6 +201,44 @@ func file_proxy_blackhole_config_proto_init() { if File_proxy_blackhole_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_blackhole_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NoneResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_blackhole_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_blackhole_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/blackhole/errors.generated.go b/proxy/blackhole/errors.generated.go new file mode 100644 index 00000000..344f86cb --- /dev/null +++ b/proxy/blackhole/errors.generated.go @@ -0,0 +1,9 @@ +package blackhole + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/dns/config.pb.go b/proxy/dns/config.pb.go index 22f4cbfc..653fe141 100644 --- a/proxy/dns/config.pb.go +++ b/proxy/dns/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/dns/config.proto package dns @@ -28,17 +28,17 @@ type Config struct { // Server is the DNS server address. If specified, this address overrides the // original one. - Server *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` - UserLevel uint32 `protobuf:"varint,2,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` - Non_IPQuery string `protobuf:"bytes,3,opt,name=non_IP_query,json=nonIPQuery,proto3" json:"non_IP_query,omitempty"` - BlockTypes []int32 `protobuf:"varint,4,rep,packed,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"` + Server *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` + UserLevel uint32 `protobuf:"varint,2,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_dns_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_dns_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -49,7 +49,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_dns_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -78,20 +78,6 @@ func (x *Config) GetUserLevel() uint32 { return 0 } -func (x *Config) GetNon_IPQuery() string { - if x != nil { - return x.Non_IPQuery - } - return "" -} - -func (x *Config) GetBlockTypes() []int32 { - if x != nil { - return x.BlockTypes - } - return nil -} - var File_proxy_dns_config_proto protoreflect.FileDescriptor var file_proxy_dns_config_proto_rawDesc = []byte{ @@ -99,22 +85,18 @@ var file_proxy_dns_config_proto_rawDesc = []byte{ 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9d, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 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, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x6f, 0x6e, 0x5f, 0x49, 0x50, 0x5f, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x6f, 0x6e, 0x49, 0x50, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x54, 0x79, 0x70, 0x65, 0x73, 0x42, 0x4c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x23, - 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, - 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0e, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x31, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 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, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x42, 0x4c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x23, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6e, 0x73, 0xaa, + 0x02, 0x0e, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, 0x6e, 0x73, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -130,7 +112,7 @@ func file_proxy_dns_config_proto_rawDescGZIP() []byte { } var file_proxy_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_dns_config_proto_goTypes = []any{ +var file_proxy_dns_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.proxy.dns.Config (*net.Endpoint)(nil), // 1: xray.common.net.Endpoint } @@ -148,6 +130,20 @@ func file_proxy_dns_config_proto_init() { if File_proxy_dns_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_dns_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/dns/config.proto b/proxy/dns/config.proto index af2aad8c..0b324725 100644 --- a/proxy/dns/config.proto +++ b/proxy/dns/config.proto @@ -13,6 +13,4 @@ message Config { // original one. xray.common.net.Endpoint server = 1; uint32 user_level = 2; - string non_IP_query = 3; - repeated int32 block_types = 4; } diff --git a/proxy/dns/dns.go b/proxy/dns/dns.go index a35854ef..ae123c28 100644 --- a/proxy/dns/dns.go +++ b/proxy/dns/dns.go @@ -2,14 +2,12 @@ package dns import ( "context" - go_errors "errors" "io" "sync" "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" dns_proto "github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/session" @@ -28,9 +26,6 @@ func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { h := new(Handler) if err := core.RequireFeatures(ctx, func(dnsClient dns.Client, policyManager policy.Manager) error { - core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) { - h.fdns = fdns - }) return h.Init(config.(*Config), dnsClient, policyManager) }); err != nil { return nil, err @@ -45,12 +40,9 @@ type ownLinkVerifier interface { type Handler struct { client dns.Client - fdns dns.FakeDNSEngine ownLinkVerifier ownLinkVerifier server net.Destination timeout time.Duration - nonIPQuery string - blockTypes []int32 } func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager policy.Manager) error { @@ -64,8 +56,6 @@ func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager polic if config.Server != nil { h.server = config.Server.AsDestination() } - h.nonIPQuery = config.Non_IPQuery - h.blockTypes = config.BlockTypes return nil } @@ -77,38 +67,36 @@ func parseIPQuery(b []byte) (r bool, domain string, id uint16, qType dnsmessage. var parser dnsmessage.Parser header, err := parser.Start(b) if err != nil { - errors.LogInfoInner(context.Background(), err, "parser start") + newError("parser start").Base(err).WriteToLog() return } id = header.ID q, err := parser.Question() if err != nil { - errors.LogInfoInner(context.Background(), err, "question") + newError("question").Base(err).WriteToLog() return } - domain = q.Name.String() qType = q.Type if qType != dnsmessage.TypeA && qType != dnsmessage.TypeAAAA { return } + domain = q.Name.String() r = true return } // Process implements proxy.Outbound. func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("invalid outbound") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("invalid outbound") } - ob.Name = "dns" - srcNetwork := ob.Target.Network + srcNetwork := outbound.Target.Network - dest := ob.Target + dest := outbound.Target if h.server.Network != net.Network_Unknown { dest.Network = h.server.Network } @@ -119,7 +107,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet. dest.Port = h.server.Port } - errors.LogInfo(ctx, "handling DNS traffic to ", dest) + newError("handling DNS traffic to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn := &outboundConn{ dialer: func() (stat.Connection, error) { @@ -160,10 +148,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet. } } - if session.TimeoutOnlyFromContext(ctx) { - ctx, _ = context.WithCancel(context.Background()) - } - ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, h.timeout) @@ -184,27 +168,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet. if !h.isOwnLink(ctx) { isIPQuery, domain, id, qType := parseIPQuery(b.Bytes()) - if len(h.blockTypes) > 0 { - for _, blocktype := range h.blockTypes { - if blocktype == int32(qType) { - if h.nonIPQuery == "reject" { - go h.rejectNonIPQuery(id, qType, domain, writer) - } - errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain) - return nil - } - } - } if isIPQuery { go h.handleIPQuery(id, qType, domain, writer) - } - if isIPQuery || h.nonIPQuery == "drop" { - b.Release() - continue - } - if h.nonIPQuery == "reject" { - go h.rejectNonIPQuery(id, qType, domain, writer) - b.Release() continue } } @@ -235,7 +200,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet. } if err := task.Run(ctx, request, response); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil @@ -245,18 +210,17 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, var ips []net.IP var err error - var ttl4 uint32 - var ttl6 uint32 + var ttl uint32 = 600 switch qType { case dnsmessage.TypeA: - ips, ttl4, err = h.client.LookupIP(domain, dns.IPOption{ + ips, err = h.client.LookupIP(domain, dns.IPOption{ IPv4Enable: true, IPv6Enable: false, FakeEnable: true, }) case dnsmessage.TypeAAAA: - ips, ttl6, err = h.client.LookupIP(domain, dns.IPOption{ + ips, err = h.client.LookupIP(domain, dns.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: true, @@ -264,8 +228,8 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, } rcode := dns.RCodeFromError(err) - if rcode == 0 && len(ips) == 0 && !go_errors.Is(err, dns.ErrEmptyResponse) { - errors.LogInfoInner(context.Background(), err, "ip query") + if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse { + newError("ip query").Base(err).WriteToLog() return } @@ -299,61 +263,28 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, })) common.Must(builder.StartAnswers()) - rHeader4 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl4} - rHeader6 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl6} + rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl} for _, ip := range ips { if len(ip) == net.IPv4len { var r dnsmessage.AResource copy(r.A[:], ip) - common.Must(builder.AResource(rHeader4, r)) + common.Must(builder.AResource(rHeader, r)) } else { var r dnsmessage.AAAAResource copy(r.AAAA[:], ip) - common.Must(builder.AAAAResource(rHeader6, r)) + common.Must(builder.AAAAResource(rHeader, r)) } } msgBytes, err := builder.Finish() if err != nil { - errors.LogInfoInner(context.Background(), err, "pack message") + newError("pack message").Base(err).WriteToLog() b.Release() return } b.Resize(0, int32(len(msgBytes))) if err := writer.WriteMessage(b); err != nil { - errors.LogInfoInner(context.Background(), err, "write IP answer") - } -} - -func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) { - b := buf.New() - rawBytes := b.Extend(buf.Size) - builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{ - ID: id, - RCode: dnsmessage.RCodeRefused, - RecursionAvailable: true, - RecursionDesired: true, - Response: true, - Authoritative: true, - }) - builder.EnableCompression() - common.Must(builder.StartQuestions()) - common.Must(builder.Question(dnsmessage.Question{ - Name: dnsmessage.MustNewName(domain), - Class: dnsmessage.ClassINET, - Type: qType, - })) - - msgBytes, err := builder.Finish() - if err != nil { - errors.LogInfoInner(context.Background(), err, "pack reject message") - b.Release() - return - } - b.Resize(0, int32(len(msgBytes))) - - if err := writer.WriteMessage(b); err != nil { - errors.LogInfoInner(context.Background(), err, "write reject answer") + newError("write IP answer").Base(err).WriteToLog() } } @@ -381,7 +312,7 @@ func (c *outboundConn) Write(b []byte) (int, error) { if c.conn == nil { if err := c.dial(); err != nil { c.access.Unlock() - errors.LogWarningInner(context.Background(), err, "failed to dial outbound connection") + newError("failed to dial outbound connection").Base(err).AtWarning().WriteToLog() return len(b), nil } } diff --git a/proxy/dns/dns_test.go b/proxy/dns/dns_test.go index 2a005d68..ba6ee00e 100644 --- a/proxy/dns/dns_test.go +++ b/proxy/dns/dns_test.go @@ -91,17 +91,15 @@ func TestUDPDNSTunnel(t *testing.T) { config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&dnsapp.Config{ - NameServer: []*dnsapp.NameServer{ + NameServers: []*net.Endpoint{ { - Address: &net.Endpoint{ - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, - }, + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, }, - Port: uint32(port), }, + Port: uint32(port), }, }, }), diff --git a/proxy/dns/errors.generated.go b/proxy/dns/errors.generated.go new file mode 100644 index 00000000..d7375a9b --- /dev/null +++ b/proxy/dns/errors.generated.go @@ -0,0 +1,9 @@ +package dns + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/dokodemo/config.pb.go b/proxy/dokodemo/config.pb.go index b30c123e..82eb0704 100644 --- a/proxy/dokodemo/config.pb.go +++ b/proxy/dokodemo/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/dokodemo/config.proto package dokodemo @@ -29,16 +29,25 @@ type Config struct { Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` // List of networks that the Dokodemo accepts. - Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"` - FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"` - UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` + // Deprecated. Use networks. + // + // Deprecated: Do not use. + NetworkList *net.NetworkList `protobuf:"bytes,3,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"` + // List of networks that the Dokodemo accepts. + Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"` + // Deprecated: Do not use. + Timeout uint32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"` + FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"` + UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_dokodemo_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_dokodemo_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -49,7 +58,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_dokodemo_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -78,6 +87,14 @@ func (x *Config) GetPort() uint32 { return 0 } +// Deprecated: Do not use. +func (x *Config) GetNetworkList() *net.NetworkList { + if x != nil { + return x.NetworkList + } + return nil +} + func (x *Config) GetNetworks() []net.Network { if x != nil { return x.Networks @@ -85,6 +102,14 @@ func (x *Config) GetNetworks() []net.Network { return nil } +// Deprecated: Do not use. +func (x *Config) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + func (x *Config) GetFollowRedirect() bool { if x != nil { return x.FollowRedirect @@ -108,26 +133,33 @@ var file_proxy_dokodemo_config_proto_rawDesc = []byte{ 0x6d, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb4, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 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, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x34, 0x0a, 0x08, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, - 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x65, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x66, 0x6f, 0x6c, - 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x5b, 0x0a, 0x17, 0x63, 0x6f, - 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6f, 0x6b, - 0x6f, 0x64, 0x65, 0x6d, 0x6f, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, - 0x6f, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, - 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x43, 0x0a, 0x0c, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, + 0x74, 0x12, 0x34, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1c, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, + 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, + 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x5b, 0x0a, + 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x64, 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6f, 0x6b, 0x6f, + 0x64, 0x65, 0x6d, 0x6f, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x44, 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -143,19 +175,21 @@ func file_proxy_dokodemo_config_proto_rawDescGZIP() []byte { } var file_proxy_dokodemo_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_dokodemo_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.proxy.dokodemo.Config - (*net.IPOrDomain)(nil), // 1: xray.common.net.IPOrDomain - (net.Network)(0), // 2: xray.common.net.Network +var file_proxy_dokodemo_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.proxy.dokodemo.Config + (*net.IPOrDomain)(nil), // 1: xray.common.net.IPOrDomain + (*net.NetworkList)(nil), // 2: xray.common.net.NetworkList + (net.Network)(0), // 3: xray.common.net.Network } var file_proxy_dokodemo_config_proto_depIdxs = []int32{ 1, // 0: xray.proxy.dokodemo.Config.address:type_name -> xray.common.net.IPOrDomain - 2, // 1: xray.proxy.dokodemo.Config.networks:type_name -> xray.common.net.Network - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 2, // 1: xray.proxy.dokodemo.Config.network_list:type_name -> xray.common.net.NetworkList + 3, // 2: xray.proxy.dokodemo.Config.networks:type_name -> xray.common.net.Network + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_proxy_dokodemo_config_proto_init() } @@ -163,6 +197,20 @@ func file_proxy_dokodemo_config_proto_init() { if File_proxy_dokodemo_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_dokodemo_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/dokodemo/config.proto b/proxy/dokodemo/config.proto index 2b60ea6f..6b5b262b 100644 --- a/proxy/dokodemo/config.proto +++ b/proxy/dokodemo/config.proto @@ -13,9 +13,13 @@ message Config { xray.common.net.IPOrDomain address = 1; uint32 port = 2; + // List of networks that the Dokodemo accepts. + // Deprecated. Use networks. + xray.common.net.NetworkList network_list = 3 [deprecated = true]; // List of networks that the Dokodemo accepts. repeated xray.common.net.Network networks = 7; + uint32 timeout = 4 [deprecated = true]; bool follow_redirect = 5; uint32 user_level = 6; } diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 4fb431f4..d0fb69f9 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -1,13 +1,14 @@ package dokodemo +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - "runtime" "sync/atomic" + "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" @@ -18,7 +19,6 @@ import ( "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet/stat" - "github.com/xtls/xray-core/transport/internet/tls" ) func init() { @@ -41,8 +41,8 @@ type DokodemoDoor struct { // Init initializes the DokodemoDoor instance with necessary parameters. func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session.Sockopt) error { - if len(config.Networks) == 0 { - return errors.New("no network specified") + if (config.NetworkList == nil || len(config.NetworkList.Network) == 0) && len(config.Networks) == 0 { + return newError("no network specified") } d.config = config d.address = config.GetPredefinedAddress() @@ -55,18 +55,29 @@ func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session. // Network implements proxy.Inbound. func (d *DokodemoDoor) Network() []net.Network { - return d.config.Networks + if len(d.config.Networks) > 0 { + return d.config.Networks + } + + return d.config.NetworkList.Network } func (d *DokodemoDoor) policy() policy.Session { config := d.config p := d.policyManager.ForLevel(config.UserLevel) + if config.Timeout > 0 && config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second + } return p } +type hasHandshakeAddress interface { + HandshakeAddress() net.Address +} + // Process implements proxy.Inbound. func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { - errors.LogDebug(ctx, "processing connection from: ", conn.RemoteAddr()) + newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WriteToLog(session.ExportIDToError(ctx)) dest := net.Destination{ Network: network, Address: d.address, @@ -75,34 +86,26 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st destinationOverridden := false if d.config.FollowRedirect { - outbounds := session.OutboundsFromContext(ctx) - if len(outbounds) > 0 { - ob := outbounds[len(outbounds)-1] - if ob.Target.IsValid() { - dest = ob.Target + if outbound := session.OutboundFromContext(ctx); outbound != nil && outbound.Target.IsValid() { + dest = outbound.Target + destinationOverridden = true + } else if handshake, ok := conn.(hasHandshakeAddress); ok { + addr := handshake.HandshakeAddress() + if addr != nil { + dest.Address = addr destinationOverridden = true } } - if tlsConn, ok := conn.(tls.Interface); ok && !destinationOverridden { - if serverName := tlsConn.HandshakeContextServerName(ctx); serverName != "" { - dest.Address = net.DomainAddress(serverName) - destinationOverridden = true - ctx = session.ContextWithMitmServerName(ctx, serverName) - } - if tlsConn.NegotiatedProtocol() != "h2" { - ctx = session.ContextWithMitmAlpn11(ctx, true) - } - } } if !dest.IsValid() || dest.Address == nil { - return errors.New("unable to get destination") + return newError("unable to get destination") } inbound := session.InboundFromContext(ctx) - inbound.Name = "dokodemo-door" - inbound.CanSpliceCopy = 1 - inbound.User = &protocol.MemoryUser{ - Level: d.config.UserLevel, + if inbound != nil { + inbound.User = &protocol.MemoryUser{ + Level: d.config.UserLevel, + } } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ @@ -111,7 +114,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st Status: log.AccessAccepted, Reason: "", }) - errors.LogInfo(ctx, "received request for ", conn.RemoteAddr()) + newError("received request for ", conn.RemoteAddr()).WriteToLog(session.ExportIDToError(ctx)) plcy := d.policy() ctx, cancel := context.WithCancel(ctx) @@ -124,7 +127,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st ctx = policy.ContextWithBufferPolicy(ctx, plcy.Buffer) link, err := dispatcher.Dispatch(ctx, dest) if err != nil { - return errors.New("failed to dispatch request").Base(err) + return newError("failed to dispatch request").Base(err) } requestCount := int32(1) @@ -142,12 +145,16 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st reader = buf.NewReader(conn) } if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport request").Base(err) + return newError("failed to transport request").Base(err) } return nil } + tproxyRequest := func() error { + return nil + } + var writer buf.Writer if network == net.Network_TCP { writer = buf.NewWriter(conn) @@ -177,12 +184,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st return err } writer = NewPacketWriter(pConn, &dest, mark, back) - defer func() { - runtime.Gosched() - common.Interrupt(link.Reader) // maybe duplicated - runtime.Gosched() - writer.(*PacketWriter).Close() // close fake UDP conns - }() + defer writer.(*PacketWriter).Close() /* sockopt := &internet.SocketConfig{ Tproxy: internet.SocketConfig_TProxy, @@ -210,7 +212,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st } }() if err := buf.Copy(tReader, link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport request (TPROXY conn)").Base(err) + return newError("failed to transport request (TPROXY conn)").Base(err) } return nil } @@ -221,25 +223,18 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st responseDone := func() error { defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) - if network == net.Network_UDP && destinationOverridden { - buf.Copy(link.Reader, writer) // respect upload's timeout - return nil - } - if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport response").Base(err) + return newError("failed to transport response").Base(err) } return nil } - if err := task.Run(ctx, - task.OnSuccess(func() error { return task.Run(ctx, requestDone) }, task.Close(link.Writer)), - responseDone); err != nil { - runtime.Gosched() - common.Interrupt(link.Writer) - runtime.Gosched() + if err := task.Run(ctx, task.OnSuccess(func() error { + return task.Run(ctx, requestDone, tproxyRequest) + }, task.Close(link.Writer)), responseDone); err != nil { common.Interrupt(link.Reader) - return errors.New("connection ends").Base(err) + common.Interrupt(link.Writer) + return newError("connection ends").Base(err) } return nil @@ -282,7 +277,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { w.mark, ) if err != nil { - errors.LogInfo(context.Background(), err.Error()) + newError(err).WriteToLog() b.Release() continue } @@ -290,7 +285,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { } _, err = conn.WriteTo(b.Bytes(), w.back) if err != nil { - errors.LogInfo(context.Background(), err.Error()) + newError(err).WriteToLog() w.conns[*b.UDP] = nil conn.Close() } diff --git a/proxy/dokodemo/errors.generated.go b/proxy/dokodemo/errors.generated.go new file mode 100644 index 00000000..cc4fd09c --- /dev/null +++ b/proxy/dokodemo/errors.generated.go @@ -0,0 +1,9 @@ +package dokodemo + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/freedom/config.go b/proxy/freedom/config.go index 2e67244b..61cc6ad5 100644 --- a/proxy/freedom/config.go +++ b/proxy/freedom/config.go @@ -1,44 +1,5 @@ package freedom -var strategy = [][]byte{ - // name strategy, prefer, fallback - {0, 0, 0}, // AsIs none, /, / - {1, 0, 0}, // UseIP use, both, none - {1, 4, 0}, // UseIPv4 use, 4, none - {1, 6, 0}, // UseIPv6 use, 6, none - {1, 4, 6}, // UseIPv4v6 use, 4, 6 - {1, 6, 4}, // UseIPv6v4 use, 6, 4 - {2, 0, 0}, // ForceIP force, both, none - {2, 4, 0}, // ForceIPv4 force, 4, none - {2, 6, 0}, // ForceIPv6 force, 6, none - {2, 4, 6}, // ForceIPv4v6 force, 4, 6 - {2, 6, 4}, // ForceIPv6v4 force, 6, 4 -} - -func (c *Config) hasStrategy() bool { - return strategy[c.DomainStrategy][0] != 0 -} - -func (c *Config) forceIP() bool { - return strategy[c.DomainStrategy][0] == 2 -} - -func (c *Config) preferIP4() bool { - return strategy[c.DomainStrategy][1] == 4 || strategy[c.DomainStrategy][1] == 0 -} - -func (c *Config) preferIP6() bool { - return strategy[c.DomainStrategy][1] == 6 || strategy[c.DomainStrategy][1] == 0 -} - -func (c *Config) hasFallback() bool { - return strategy[c.DomainStrategy][2] != 0 -} - -func (c *Config) fallbackIP4() bool { - return strategy[c.DomainStrategy][2] == 4 -} - -func (c *Config) fallbackIP6() bool { - return strategy[c.DomainStrategy][2] == 6 +func (c *Config) useIP() bool { + return c.DomainStrategy == Config_USE_IP || c.DomainStrategy == Config_USE_IP4 || c.DomainStrategy == Config_USE_IP6 } diff --git a/proxy/freedom/config.pb.go b/proxy/freedom/config.pb.go index 83bb15d6..5c95bce7 100644 --- a/proxy/freedom/config.pb.go +++ b/proxy/freedom/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/freedom/config.proto package freedom @@ -24,46 +24,25 @@ const ( type Config_DomainStrategy int32 const ( - Config_AS_IS Config_DomainStrategy = 0 - Config_USE_IP Config_DomainStrategy = 1 - Config_USE_IP4 Config_DomainStrategy = 2 - Config_USE_IP6 Config_DomainStrategy = 3 - Config_USE_IP46 Config_DomainStrategy = 4 - Config_USE_IP64 Config_DomainStrategy = 5 - Config_FORCE_IP Config_DomainStrategy = 6 - Config_FORCE_IP4 Config_DomainStrategy = 7 - Config_FORCE_IP6 Config_DomainStrategy = 8 - Config_FORCE_IP46 Config_DomainStrategy = 9 - Config_FORCE_IP64 Config_DomainStrategy = 10 + Config_AS_IS Config_DomainStrategy = 0 + Config_USE_IP Config_DomainStrategy = 1 + Config_USE_IP4 Config_DomainStrategy = 2 + Config_USE_IP6 Config_DomainStrategy = 3 ) // Enum value maps for Config_DomainStrategy. var ( Config_DomainStrategy_name = map[int32]string{ - 0: "AS_IS", - 1: "USE_IP", - 2: "USE_IP4", - 3: "USE_IP6", - 4: "USE_IP46", - 5: "USE_IP64", - 6: "FORCE_IP", - 7: "FORCE_IP4", - 8: "FORCE_IP6", - 9: "FORCE_IP46", - 10: "FORCE_IP64", + 0: "AS_IS", + 1: "USE_IP", + 2: "USE_IP4", + 3: "USE_IP6", } Config_DomainStrategy_value = map[string]int32{ - "AS_IS": 0, - "USE_IP": 1, - "USE_IP4": 2, - "USE_IP6": 3, - "USE_IP46": 4, - "USE_IP64": 5, - "FORCE_IP": 6, - "FORCE_IP4": 7, - "FORCE_IP6": 8, - "FORCE_IP46": 9, - "FORCE_IP64": 10, + "AS_IS": 0, + "USE_IP": 1, + "USE_IP4": 2, + "USE_IP6": 3, } ) @@ -91,7 +70,7 @@ func (x Config_DomainStrategy) Number() protoreflect.EnumNumber { // Deprecated: Use Config_DomainStrategy.Descriptor instead. func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) { - return file_proxy_freedom_config_proto_rawDescGZIP(), []int{3, 0} + return file_proxy_freedom_config_proto_rawDescGZIP(), []int{1, 0} } type DestinationOverride struct { @@ -104,9 +83,11 @@ type DestinationOverride struct { func (x *DestinationOverride) Reset() { *x = DestinationOverride{} - mi := &file_proxy_freedom_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_freedom_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DestinationOverride) String() string { @@ -117,7 +98,7 @@ func (*DestinationOverride) ProtoMessage() {} func (x *DestinationOverride) ProtoReflect() protoreflect.Message { mi := &file_proxy_freedom_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -139,186 +120,25 @@ func (x *DestinationOverride) GetServer() *protocol.ServerEndpoint { return nil } -type Fragment struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PacketsFrom uint64 `protobuf:"varint,1,opt,name=packets_from,json=packetsFrom,proto3" json:"packets_from,omitempty"` - PacketsTo uint64 `protobuf:"varint,2,opt,name=packets_to,json=packetsTo,proto3" json:"packets_to,omitempty"` - LengthMin uint64 `protobuf:"varint,3,opt,name=length_min,json=lengthMin,proto3" json:"length_min,omitempty"` - LengthMax uint64 `protobuf:"varint,4,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"` - IntervalMin uint64 `protobuf:"varint,5,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"` - IntervalMax uint64 `protobuf:"varint,6,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"` -} - -func (x *Fragment) Reset() { - *x = Fragment{} - mi := &file_proxy_freedom_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Fragment) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Fragment) ProtoMessage() {} - -func (x *Fragment) ProtoReflect() protoreflect.Message { - mi := &file_proxy_freedom_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 Fragment.ProtoReflect.Descriptor instead. -func (*Fragment) Descriptor() ([]byte, []int) { - return file_proxy_freedom_config_proto_rawDescGZIP(), []int{1} -} - -func (x *Fragment) GetPacketsFrom() uint64 { - if x != nil { - return x.PacketsFrom - } - return 0 -} - -func (x *Fragment) GetPacketsTo() uint64 { - if x != nil { - return x.PacketsTo - } - return 0 -} - -func (x *Fragment) GetLengthMin() uint64 { - if x != nil { - return x.LengthMin - } - return 0 -} - -func (x *Fragment) GetLengthMax() uint64 { - if x != nil { - return x.LengthMax - } - return 0 -} - -func (x *Fragment) GetIntervalMin() uint64 { - if x != nil { - return x.IntervalMin - } - return 0 -} - -func (x *Fragment) GetIntervalMax() uint64 { - if x != nil { - return x.IntervalMax - } - return 0 -} - -type Noise struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LengthMin uint64 `protobuf:"varint,1,opt,name=length_min,json=lengthMin,proto3" json:"length_min,omitempty"` - LengthMax uint64 `protobuf:"varint,2,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"` - DelayMin uint64 `protobuf:"varint,3,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` - DelayMax uint64 `protobuf:"varint,4,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` - Packet []byte `protobuf:"bytes,5,opt,name=packet,proto3" json:"packet,omitempty"` -} - -func (x *Noise) Reset() { - *x = Noise{} - mi := &file_proxy_freedom_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Noise) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Noise) ProtoMessage() {} - -func (x *Noise) ProtoReflect() protoreflect.Message { - mi := &file_proxy_freedom_config_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Noise.ProtoReflect.Descriptor instead. -func (*Noise) Descriptor() ([]byte, []int) { - return file_proxy_freedom_config_proto_rawDescGZIP(), []int{2} -} - -func (x *Noise) GetLengthMin() uint64 { - if x != nil { - return x.LengthMin - } - return 0 -} - -func (x *Noise) GetLengthMax() uint64 { - if x != nil { - return x.LengthMax - } - return 0 -} - -func (x *Noise) GetDelayMin() uint64 { - if x != nil { - return x.DelayMin - } - return 0 -} - -func (x *Noise) GetDelayMax() uint64 { - if x != nil { - return x.DelayMax - } - return 0 -} - -func (x *Noise) GetPacket() []byte { - if x != nil { - return x.Packet - } - return nil -} - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - DomainStrategy Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.freedom.Config_DomainStrategy" json:"domain_strategy,omitempty"` - DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` - UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` - Fragment *Fragment `protobuf:"bytes,5,opt,name=fragment,proto3" json:"fragment,omitempty"` - ProxyProtocol uint32 `protobuf:"varint,6,opt,name=proxy_protocol,json=proxyProtocol,proto3" json:"proxy_protocol,omitempty"` - Noises []*Noise `protobuf:"bytes,7,rep,name=noises,proto3" json:"noises,omitempty"` + DomainStrategy Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.freedom.Config_DomainStrategy" json:"domain_strategy,omitempty"` + // Deprecated: Do not use. + Timeout uint32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` + UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_freedom_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_freedom_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -328,8 +148,8 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_proxy_freedom_config_proto_msgTypes[3] - if x != nil { + mi := &file_proxy_freedom_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -341,7 +161,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_proxy_freedom_config_proto_rawDescGZIP(), []int{3} + return file_proxy_freedom_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetDomainStrategy() Config_DomainStrategy { @@ -351,6 +171,14 @@ func (x *Config) GetDomainStrategy() Config_DomainStrategy { return Config_AS_IS } +// Deprecated: Do not use. +func (x *Config) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + func (x *Config) GetDestinationOverride() *DestinationOverride { if x != nil { return x.DestinationOverride @@ -365,27 +193,6 @@ func (x *Config) GetUserLevel() uint32 { return 0 } -func (x *Config) GetFragment() *Fragment { - if x != nil { - return x.Fragment - } - return nil -} - -func (x *Config) GetProxyProtocol() uint32 { - if x != nil { - return x.ProxyProtocol - } - return 0 -} - -func (x *Config) GetNoises() []*Noise { - if x != nil { - return x.Noises - } - return nil -} - var File_proxy_freedom_config_proto protoreflect.FileDescriptor var file_proxy_freedom_config_proto_rawDesc = []byte{ @@ -399,70 +206,33 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{ 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0xd0, 0x01, 0x0a, 0x08, 0x46, 0x72, 0x61, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, - 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x70, 0x61, 0x63, - 0x6b, 0x65, 0x74, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63, 0x6b, - 0x65, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x70, 0x61, - 0x63, 0x6b, 0x65, 0x74, 0x73, 0x54, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x4d, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x4d, 0x61, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0x97, 0x01, 0x0a, 0x05, - 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, - 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x4d, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x6d, - 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x4d, 0x61, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6e, - 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x61, 0x78, 0x12, 0x16, 0x0a, - 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, - 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x97, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, - 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, - 0x74, 0x65, 0x67, 0x79, 0x12, 0x5a, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x13, 0x64, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, - 0x38, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, - 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, - 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x12, 0x31, 0x0a, 0x06, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, - 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x52, 0x06, 0x6e, 0x6f, 0x69, - 0x73, 0x65, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, - 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, - 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, - 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, - 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, - 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, - 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, - 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, - 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, - 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, - 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, - 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65, - 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0xb8, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, + 0x6d, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1c, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x5a, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x13, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x22, 0x41, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, + 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, + 0x36, 0x10, 0x03, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2f, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -478,26 +248,22 @@ func file_proxy_freedom_config_proto_rawDescGZIP() []byte { } var file_proxy_freedom_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proxy_freedom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_proxy_freedom_config_proto_goTypes = []any{ +var file_proxy_freedom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proxy_freedom_config_proto_goTypes = []interface{}{ (Config_DomainStrategy)(0), // 0: xray.proxy.freedom.Config.DomainStrategy (*DestinationOverride)(nil), // 1: xray.proxy.freedom.DestinationOverride - (*Fragment)(nil), // 2: xray.proxy.freedom.Fragment - (*Noise)(nil), // 3: xray.proxy.freedom.Noise - (*Config)(nil), // 4: xray.proxy.freedom.Config - (*protocol.ServerEndpoint)(nil), // 5: xray.common.protocol.ServerEndpoint + (*Config)(nil), // 2: xray.proxy.freedom.Config + (*protocol.ServerEndpoint)(nil), // 3: xray.common.protocol.ServerEndpoint } var file_proxy_freedom_config_proto_depIdxs = []int32{ - 5, // 0: xray.proxy.freedom.DestinationOverride.server:type_name -> xray.common.protocol.ServerEndpoint + 3, // 0: xray.proxy.freedom.DestinationOverride.server:type_name -> xray.common.protocol.ServerEndpoint 0, // 1: xray.proxy.freedom.Config.domain_strategy:type_name -> xray.proxy.freedom.Config.DomainStrategy 1, // 2: xray.proxy.freedom.Config.destination_override:type_name -> xray.proxy.freedom.DestinationOverride - 2, // 3: xray.proxy.freedom.Config.fragment:type_name -> xray.proxy.freedom.Fragment - 3, // 4: xray.proxy.freedom.Config.noises:type_name -> xray.proxy.freedom.Noise - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_proxy_freedom_config_proto_init() } @@ -505,13 +271,39 @@ func file_proxy_freedom_config_proto_init() { if File_proxy_freedom_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_freedom_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DestinationOverride); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_freedom_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proxy_freedom_config_proto_rawDesc, NumEnums: 1, - NumMessages: 4, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/proxy/freedom/config.proto b/proxy/freedom/config.proto index 5d05ab9e..7578a43f 100644 --- a/proxy/freedom/config.proto +++ b/proxy/freedom/config.proto @@ -12,40 +12,15 @@ message DestinationOverride { xray.common.protocol.ServerEndpoint server = 1; } -message Fragment { - uint64 packets_from = 1; - uint64 packets_to = 2; - uint64 length_min = 3; - uint64 length_max = 4; - uint64 interval_min = 5; - uint64 interval_max = 6; -} -message Noise { - uint64 length_min = 1; - uint64 length_max = 2; - uint64 delay_min = 3; - uint64 delay_max = 4; - bytes packet = 5; -} - message Config { enum DomainStrategy { AS_IS = 0; USE_IP = 1; USE_IP4 = 2; USE_IP6 = 3; - USE_IP46 = 4; - USE_IP64 = 5; - FORCE_IP = 6; - FORCE_IP4 = 7; - FORCE_IP6 = 8; - FORCE_IP46 = 9; - FORCE_IP64 = 10; } DomainStrategy domain_strategy = 1; + uint32 timeout = 2 [deprecated = true]; DestinationOverride destination_override = 3; uint32 user_level = 4; - Fragment fragment = 5; - uint32 proxy_protocol = 6; - repeated Noise noises = 7; } diff --git a/proxy/freedom/errors.generated.go b/proxy/freedom/errors.generated.go new file mode 100644 index 00000000..344fbb2f --- /dev/null +++ b/proxy/freedom/errors.generated.go @@ -0,0 +1,9 @@ +package freedom + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 779992f7..15ebc22b 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -1,19 +1,15 @@ package freedom +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" - "crypto/rand" - "io" "time" - "github.com/pires/go-proxyproto" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/retry" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/signal" @@ -22,15 +18,11 @@ import ( "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/stats" - "github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" - "github.com/xtls/xray-core/transport/internet/tls" ) -var useSplice bool - func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { h := new(Handler) @@ -41,12 +33,6 @@ func init() { } return h, nil })) - const defaultFlagValue = "NOT_DEFINED_AT_ALL" - value := platform.NewEnvFlag(platform.UseFreedomSplice).GetValue(func() string { return defaultFlagValue }) - switch value { - case defaultFlagValue, "auto", "enable": - useSplice = true - } } // Handler handles Freedom connections. @@ -67,24 +53,35 @@ func (h *Handler) Init(config *Config, pm policy.Manager, d dns.Client) error { func (h *Handler) policy() policy.Session { p := h.policyManager.ForLevel(h.config.UserLevel) + if h.config.Timeout > 0 && h.config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(h.config.Timeout) * time.Second + } return p } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { - ips, _, err := h.dns.LookupIP(domain, dns.IPOption{ - IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && h.config.preferIP4(), - IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && h.config.preferIP6(), - }) - { // Resolve fallback - if (len(ips) == 0 || err != nil) && h.config.hasFallback() && localAddr == nil { - ips, _, err = h.dns.LookupIP(domain, dns.IPOption{ - IPv4Enable: h.config.fallbackIP4(), - IPv6Enable: h.config.fallbackIP6(), - }) + var option dns.IPOption = dns.IPOption{ + IPv4Enable: true, + IPv6Enable: true, + FakeEnable: false, + } + if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) { + option = dns.IPOption{ + IPv4Enable: true, + IPv6Enable: false, + FakeEnable: false, + } + } else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) { + option = dns.IPOption{ + IPv4Enable: false, + IPv6Enable: true, + FakeEnable: false, } } + + ips, err := h.dns.LookupIP(domain, option) if err != nil { - errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", domain) + newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) } if len(ips) == 0 { return nil @@ -103,16 +100,11 @@ func isValidAddress(addr *net.IPOrDomain) bool { // Process implements proxy.Outbound. func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified.") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified.") } - ob.Name = "freedom" - ob.CanSpliceCopy = 1 - inbound := session.InboundFromContext(ctx) - - destination := ob.Target + destination := outbound.Target UDPOverride := net.UDPDestination(nil, 0) if h.config.DestinationOverride != nil { server := h.config.DestinationOverride.Server @@ -125,6 +117,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte UDPOverride.Port = destination.Port } } + newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx)) input := link.Reader output := link.Writer @@ -132,7 +125,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte var conn stat.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { dialDest := destination - if h.config.hasStrategy() && dialDest.Address.Family().IsDomain() { + if h.config.useIP() && dialDest.Address.Family().IsDomain() { ip := h.resolveIP(ctx, dialDest.Address.Domain(), dialer.Address()) if ip != nil { dialDest = net.Destination{ @@ -140,9 +133,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte Address: ip, Port: dialDest.Port, } - errors.LogInfo(ctx, "dialing to ", dialDest) - } else if h.config.forceIP() { - return dns.ErrEmptyResponse + newError("dialing to ", dialDest).WriteToLog(session.ExportIDToError(ctx)) } } @@ -150,72 +141,30 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte if err != nil { return err } - - if h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 { - version := byte(h.config.ProxyProtocol) - srcAddr := inbound.Source.RawNetAddr() - dstAddr := rawConn.RemoteAddr() - header := proxyproto.HeaderProxyFromAddrs(version, srcAddr, dstAddr) - if _, err = header.WriteTo(rawConn); err != nil { - rawConn.Close() - return err - } - } - conn = rawConn return nil }) if err != nil { - return errors.New("failed to open connection to ", destination).Base(err) + return newError("failed to open connection to ", destination).Base(err) } defer conn.Close() - errors.LogInfo(ctx, "connection opened to ", destination, ", local endpoint ", conn.LocalAddr(), ", remote endpoint ", conn.RemoteAddr()) - - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) - } plcy := h.policy() ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, plcy.Timeouts.ConnectionIdle) + timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) requestDone := func() error { defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) var writer buf.Writer if destination.Network == net.Network_TCP { - if h.config.Fragment != nil { - errors.LogDebug(ctx, "FRAGMENT", h.config.Fragment.PacketsFrom, h.config.Fragment.PacketsTo, h.config.Fragment.LengthMin, h.config.Fragment.LengthMax, - h.config.Fragment.IntervalMin, h.config.Fragment.IntervalMax) - writer = buf.NewWriter(&FragmentWriter{ - fragment: h.config.Fragment, - writer: conn, - }) - } else { - writer = buf.NewWriter(conn) - } + writer = buf.NewWriter(conn) } else { writer = NewPacketWriter(conn, h, ctx, UDPOverride) - if h.config.Noises != nil { - errors.LogDebug(ctx, "NOISE", h.config.Noises) - writer = &NoisePacketWriter{ - Writer: writer, - noises: h.config.Noises, - firstWrite: true, - UDPOverride: UDPOverride, - } - } } if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to process request").Base(err) + return newError("failed to process request").Base(err) } return nil @@ -223,17 +172,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte responseDone := func() error { defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) - if destination.Network == net.Network_TCP { - var writeConn net.Conn - var inTimer *signal.ActivityTimer - if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil && useSplice { - writeConn = inbound.Conn - inTimer = inbound.Timer - } - if !isTLSConn(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic - return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer) - } - } + var reader buf.Reader if destination.Network == net.Network_TCP { reader = buf.NewReader(conn) @@ -241,38 +180,19 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte reader = NewPacketReader(conn, UDPOverride) } if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to process response").Base(err) + return newError("failed to process response").Base(err) } + return nil } - if newCtx != nil { - ctx = newCtx - } - if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil } -func isTLSConn(conn stat.Connection) bool { - if conn != nil { - statConn, ok := conn.(*stat.CounterConnection) - if ok { - conn = statConn.Connection - } - if _, ok := conn.(*tls.Conn); ok { - return true - } - if _, ok := conn.(*tls.UConn); ok { - return true - } - } - return false -} - func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader { iConn := conn statConn, ok := iConn.(*stat.CounterConnection) @@ -335,7 +255,6 @@ func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride Context: ctx, UDPOverride: UDPOverride, } - } return &buf.SequentialWriter{Writer: conn} } @@ -364,7 +283,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { if w.UDPOverride.Port != 0 { b.UDP.Port = w.UDPOverride.Port } - if w.Handler.config.hasStrategy() && b.UDP.Address.Family().IsDomain() { + if w.Handler.config.useIP() && b.UDP.Address.Family().IsDomain() { ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil) if ip != nil { b.UDP.Address = ip @@ -390,132 +309,3 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { } return nil } - -type NoisePacketWriter struct { - buf.Writer - noises []*Noise - firstWrite bool - UDPOverride net.Destination -} - -// MultiBuffer writer with Noise before first packet -func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { - if w.firstWrite { - w.firstWrite = false - //Do not send Noise for dns requests(just to be safe) - if w.UDPOverride.Port == 53 { - return w.Writer.WriteMultiBuffer(mb) - } - var noise []byte - var err error - for _, n := range w.noises { - //User input string or base64 encoded string - if n.Packet != nil { - noise = n.Packet - } else { - //Random noise - noise, err = GenerateRandomBytes(crypto.RandBetween(int64(n.LengthMin), - int64(n.LengthMax))) - } - if err != nil { - return err - } - w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)}) - - if n.DelayMin != 0 || n.DelayMax != 0 { - time.Sleep(time.Duration(crypto.RandBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond) - } - } - - } - return w.Writer.WriteMultiBuffer(mb) -} - -type FragmentWriter struct { - fragment *Fragment - writer io.Writer - count uint64 -} - -func (f *FragmentWriter) Write(b []byte) (int, error) { - f.count++ - - if f.fragment.PacketsFrom == 0 && f.fragment.PacketsTo == 1 { - if f.count != 1 || len(b) <= 5 || b[0] != 22 { - return f.writer.Write(b) - } - recordLen := 5 + ((int(b[3]) << 8) | int(b[4])) - if len(b) < recordLen { // maybe already fragmented somehow - return f.writer.Write(b) - } - data := b[5:recordLen] - buf := make([]byte, 1024) - var hello []byte - for from := 0; ; { - to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) - if to > len(data) { - to = len(data) - } - copy(buf[:3], b) - copy(buf[5:], data[from:to]) - l := to - from - from = to - buf[3] = byte(l >> 8) - buf[4] = byte(l) - if f.fragment.IntervalMax == 0 { // combine fragmented tlshello if interval is 0 - hello = append(hello, buf[:5+l]...) - } else { - _, err := f.writer.Write(buf[:5+l]) - time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond) - if err != nil { - return 0, err - } - } - if from == len(data) { - if len(hello) > 0 { - _, err := f.writer.Write(hello) - if err != nil { - return 0, err - } - } - if len(b) > recordLen { - n, err := f.writer.Write(b[recordLen:]) - if err != nil { - return recordLen + n, err - } - } - return len(b), nil - } - } - } - - if f.fragment.PacketsFrom != 0 && (f.count < f.fragment.PacketsFrom || f.count > f.fragment.PacketsTo) { - return f.writer.Write(b) - } - for from := 0; ; { - to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) - if to > len(b) { - to = len(b) - } - n, err := f.writer.Write(b[from:to]) - from += n - time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond) - if err != nil { - return from, err - } - if from >= len(b) { - return from, nil - } - } -} - -func GenerateRandomBytes(n int64) ([]byte, error) { - b := make([]byte, n) - _, err := rand.Read(b) - // Note that err == nil only if we read len(b) bytes. - if err != nil { - return nil, err - } - - return b, nil -} diff --git a/proxy/http/client.go b/proxy/http/client.go index b1326bec..71a10e69 100644 --- a/proxy/http/client.go +++ b/proxy/http/client.go @@ -14,7 +14,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/bytespool" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/retry" @@ -52,12 +51,12 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { - return nil, errors.New("failed to get server spec").Base(err) + return nil, newError("failed to get server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { - return nil, errors.New("0 target server") + return nil, newError("0 target server") } v := core.MustFromContext(ctx) @@ -70,18 +69,15 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { // Process implements proxy.Outbound.Process. We first create a socket tunnel via HTTP CONNECT method, then redirect all inbound traffic to that tunnel. func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified.") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified.") } - ob.Name = "http" - ob.CanSpliceCopy = 2 - target := ob.Target + target := outbound.Target targetAddr := target.NetAddr() if target.Network == net.Network_UDP { - return errors.New("UDP is not supported by HTTP outbound") + return newError("UDP is not supported by HTTP outbound") } var user *protocol.MemoryUser @@ -98,7 +94,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter header, err := fillRequestHeader(ctx, c.header) if err != nil { - return errors.New("failed to fill out header").Base(err) + return newError("failed to fill out header").Base(err) } if err := retry.ExponentialBackoff(5, 100).On(func() error { @@ -118,12 +114,12 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter } return err }); err != nil { - return errors.New("failed to find an available destination").Base(err) + return newError("failed to find an available destination").Base(err) } defer func() { if err := conn.Close(); err != nil { - errors.LogInfoInner(ctx, err, "failed to closed connection") + newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } }() @@ -132,37 +128,21 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter p = c.policyManager.ForLevel(user.Level) } - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) - } - ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, p.Timeouts.ConnectionIdle) + timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle) requestFunc := func() error { defer timer.SetTimeout(p.Timeouts.DownlinkOnly) return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) } responseFunc := func() error { - ob.CanSpliceCopy = 1 defer timer.SetTimeout(p.Timeouts.UplinkOnly) return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) } - if newCtx != nil { - ctx = newCtx - } - responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil @@ -175,19 +155,14 @@ func fillRequestHeader(ctx context.Context, header []*Header) ([]*Header, error) } inbound := session.InboundFromContext(ctx) - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - - if inbound == nil || ob == nil { - return nil, errors.New("missing inbound or outbound metadata from context") - } + outbound := session.OutboundFromContext(ctx) data := struct { Source net.Destination Target net.Destination }{ Source: inbound.Source, - Target: ob.Target, + Target: outbound.Target, } filled := make([]*Header, len(header)) @@ -244,7 +219,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u if resp.StatusCode != http.StatusOK { rawConn.Close() - return nil, errors.New("Proxy responded with non 200 code: " + resp.Status) + return nil, newError("Proxy responded with non 200 code: " + resp.Status) } return rawConn, nil } @@ -276,7 +251,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u if resp.StatusCode != http.StatusOK { rawConn.Close() - return nil, errors.New("Proxy responded with non 200 code: " + resp.Status) + return nil, newError("Proxy responded with non 200 code: " + resp.Status) } return newHTTP2Conn(rawConn, pw, resp.Body), nil } @@ -309,7 +284,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u nextProto := "" if tlsConn, ok := iConn.(*tls.Conn); ok { - if err := tlsConn.HandshakeContext(ctx); err != nil { + if err := tlsConn.Handshake(); err != nil { rawConn.Close() return nil, err } @@ -346,7 +321,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u return proxyConn, err default: - return nil, errors.New("negotiated unsupported application layer protocol: " + nextProto) + return nil, newError("negotiated unsupported application layer protocol: " + nextProto) } } diff --git a/proxy/http/config.go b/proxy/http/config.go index a41bb010..00ce0795 100644 --- a/proxy/http/config.go +++ b/proxy/http/config.go @@ -1,8 +1,6 @@ package http import ( - "google.golang.org/protobuf/proto" - "github.com/xtls/xray-core/common/protocol" ) @@ -13,10 +11,6 @@ func (a *Account) Equals(another protocol.Account) bool { return false } -func (a *Account) ToProto() proto.Message { - return a -} - func (a *Account) AsAccount() (protocol.Account, error) { return a, nil } diff --git a/proxy/http/config.pb.go b/proxy/http/config.pb.go index 03e03441..813a56c8 100644 --- a/proxy/http/config.pb.go +++ b/proxy/http/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/http/config.proto package http @@ -32,9 +32,11 @@ type Account struct { func (x *Account) Reset() { *x = Account{} - mi := &file_proxy_http_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_http_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Account) String() string { @@ -45,7 +47,7 @@ func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -80,6 +82,8 @@ type ServerConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Deprecated: Do not use. + Timeout uint32 `protobuf:"varint,1,opt,name=timeout,proto3" json:"timeout,omitempty"` Accounts map[string]string `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` AllowTransparent bool `protobuf:"varint,3,opt,name=allow_transparent,json=allowTransparent,proto3" json:"allow_transparent,omitempty"` UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` @@ -87,9 +91,11 @@ type ServerConfig struct { func (x *ServerConfig) Reset() { *x = ServerConfig{} - mi := &file_proxy_http_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_http_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ServerConfig) String() string { @@ -100,7 +106,7 @@ func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -115,6 +121,14 @@ func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_http_config_proto_rawDescGZIP(), []int{1} } +// Deprecated: Do not use. +func (x *ServerConfig) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + func (x *ServerConfig) GetAccounts() map[string]string { if x != nil { return x.Accounts @@ -147,9 +161,11 @@ type Header struct { func (x *Header) Reset() { *x = Header{} - mi := &file_proxy_http_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_http_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Header) String() string { @@ -160,7 +176,7 @@ func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -202,9 +218,11 @@ type ClientConfig struct { func (x *ClientConfig) Reset() { *x = ClientConfig{} - mi := &file_proxy_http_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_http_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ClientConfig) String() string { @@ -215,7 +233,7 @@ func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -256,37 +274,39 @@ var file_proxy_http_config_proto_rawDesc = []byte{ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x22, 0xe0, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x47, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, - 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x30, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7d, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x68, 0x74, 0x74, 0x70, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x68, - 0x74, 0x74, 0x70, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x2e, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0xfe, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x1c, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, + 0x47, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x68, + 0x74, 0x74, 0x70, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x30, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x7d, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x68, + 0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x68, 0x74, 0x74, 0x70, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x68, 0x74, 0x74, + 0x70, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x48, + 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -302,7 +322,7 @@ func file_proxy_http_config_proto_rawDescGZIP() []byte { } var file_proxy_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) -var file_proxy_http_config_proto_goTypes = []any{ +var file_proxy_http_config_proto_goTypes = []interface{}{ (*Account)(nil), // 0: xray.proxy.http.Account (*ServerConfig)(nil), // 1: xray.proxy.http.ServerConfig (*Header)(nil), // 2: xray.proxy.http.Header @@ -326,6 +346,56 @@ func file_proxy_http_config_proto_init() { if File_proxy_http_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_http_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_http_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_http_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Header); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_http_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/http/config.proto b/proxy/http/config.proto index bc939df7..743c8551 100644 --- a/proxy/http/config.proto +++ b/proxy/http/config.proto @@ -15,6 +15,7 @@ message Account { // Config for HTTP proxy server. message ServerConfig { + uint32 timeout = 1 [deprecated = true]; map accounts = 2; bool allow_transparent = 3; uint32 user_level = 4; diff --git a/proxy/http/errors.generated.go b/proxy/http/errors.generated.go new file mode 100644 index 00000000..f0048165 --- /dev/null +++ b/proxy/http/errors.generated.go @@ -0,0 +1,9 @@ +package http + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/http/http.go b/proxy/http/http.go index d02cfda6..3c0d015f 100644 --- a/proxy/http/http.go +++ b/proxy/http/http.go @@ -1 +1,3 @@ package http + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/proxy/http/server.go b/proxy/http/server.go index 3721dd47..cdcf2e3a 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -2,7 +2,6 @@ package http import ( "bufio" - "bytes" "context" "encoding/base64" "io" @@ -46,6 +45,9 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { func (s *Server) policy() policy.Session { config := s.config p := s.policyManager.ForLevel(config.UserLevel) + if config.Timeout > 0 && config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second + } return p } @@ -81,37 +83,23 @@ type readerOnly struct { } func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { - return s.ProcessWithFirstbyte(ctx, network, conn, dispatcher) -} - -// Firstbyte is for forwarded conn from SOCKS inbound -// Because it needs first byte to choose protocol -// We need to add it back -// Other parts are the same as the process function -func (s *Server) ProcessWithFirstbyte(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, firstbyte ...byte) error { inbound := session.InboundFromContext(ctx) - inbound.Name = "http" - inbound.CanSpliceCopy = 2 - inbound.User = &protocol.MemoryUser{ - Level: s.config.UserLevel, - } - var reader *bufio.Reader - if len(firstbyte) > 0 { - readerWithoutFirstbyte := bufio.NewReaderSize(readerOnly{conn}, buf.Size) - multiReader := io.MultiReader(bytes.NewReader(firstbyte), readerWithoutFirstbyte) - reader = bufio.NewReaderSize(multiReader, buf.Size) - } else { - reader = bufio.NewReaderSize(readerOnly{conn}, buf.Size) + if inbound != nil { + inbound.User = &protocol.MemoryUser{ + Level: s.config.UserLevel, + } } + reader := bufio.NewReaderSize(readerOnly{conn}, buf.Size) + Start: if err := conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake)); err != nil { - errors.LogInfoInner(ctx, err, "failed to set read deadline") + newError("failed to set read deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } request, err := http.ReadRequest(reader) if err != nil { - trace := errors.New("failed to read http request").Base(err) + trace := newError("failed to read http request").Base(err) if errors.Cause(err) != io.EOF && !isTimeout(errors.Cause(err)) { trace.AtWarning() } @@ -128,9 +116,9 @@ Start: } } - errors.LogInfo(ctx, "request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]") + newError("request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]").WriteToLog(session.ExportIDToError(ctx)) if err := conn.SetReadDeadline(time.Time{}); err != nil { - errors.LogDebugInner(ctx, err, "failed to clear read deadline") + newError("failed to clear read deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } defaultPort := net.Port(80) @@ -143,7 +131,7 @@ Start: } dest, err := http_proto.ParseHost(host, defaultPort) if err != nil { - return errors.New("malformed proxy host: ", host).AtWarning().Base(err) + return newError("malformed proxy host: ", host).AtWarning().Base(err) } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: conn.RemoteAddr(), @@ -172,7 +160,7 @@ Start: func (s *Server) handleConnect(ctx context.Context, _ *http.Request, reader *bufio.Reader, conn stat.Connection, dest net.Destination, dispatcher routing.Dispatcher, inbound *session.Inbound) error { _, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) if err != nil { - return errors.New("failed to write back OK response").Base(err) + return newError("failed to write back OK response").Base(err) } plcy := s.policy() @@ -207,7 +195,6 @@ func (s *Server) handleConnect(ctx context.Context, _ *http.Request, reader *buf } responseDone := func() error { - inbound.CanSpliceCopy = 1 defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) v2writer := buf.NewWriter(conn) @@ -222,13 +209,13 @@ func (s *Server) handleConnect(ctx context.Context, _ *http.Request, reader *buf if err := task.Run(ctx, closeWriter, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil } -var errWaitAnother = errors.New("keep alive") +var errWaitAnother = newError("keep alive") func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, writer io.Writer, dest net.Destination, dispatcher routing.Dispatcher) error { if !s.config.AllowTransparent && request.URL.Host == "" { @@ -287,20 +274,20 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri requestWriter := buf.NewBufferedWriter(link.Writer) common.Must(requestWriter.SetBuffered(false)) if err := request.Write(requestWriter); err != nil { - return errors.New("failed to write whole request").Base(err).AtWarning() + return newError("failed to write whole request").Base(err).AtWarning() } return nil } responseDone := func() error { responseReader := bufio.NewReaderSize(&buf.BufferedReader{Reader: link.Reader}, buf.Size) - response, err := readResponseAndHandle100Continue(responseReader, request, writer) + response, err := http.ReadResponse(responseReader, request) if err == nil { http_proto.RemoveHopByHopHeaders(response.Header) if response.ContentLength >= 0 { response.Header.Set("Proxy-Connection", "keep-alive") response.Header.Set("Connection", "keep-alive") - response.Header.Set("Keep-Alive", "timeout=60") + response.Header.Set("Keep-Alive", "timeout=4") response.Close = false } else { response.Close = true @@ -308,7 +295,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri } defer response.Body.Close() } else { - errors.LogWarningInner(ctx, err, "failed to read response from ", request.Host) + newError("failed to read response from ", request.Host).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) response = &http.Response{ Status: "Service Unavailable", StatusCode: 503, @@ -324,7 +311,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri response.Header.Set("Proxy-Connection", "close") } if err := response.Write(writer); err != nil { - return errors.New("failed to write response").Base(err).AtWarning() + return newError("failed to write response").Base(err).AtWarning() } return nil } @@ -332,44 +319,12 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri if err := task.Run(ctx, requestDone, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return result } -// Sometimes, server might send 1xx response to client -// it should not be processed by http proxy handler, just forward it to client -func readResponseAndHandle100Continue(r *bufio.Reader, req *http.Request, writer io.Writer) (*http.Response, error) { - // have a little look of response - peekBytes, err := r.Peek(56) - if err == nil || err == bufio.ErrBufferFull { - str := string(peekBytes) - ResponseLine := strings.Split(str, "\r\n")[0] - _, status, _ := strings.Cut(ResponseLine, " ") - // only handle 1xx response - if strings.HasPrefix(status, "1") { - ResponseHeader1xx := []byte{} - // read until \r\n\r\n (end of http response header) - for { - data, err := r.ReadSlice('\n') - if err != nil { - return nil, errors.New("failed to read http 1xx response").Base(err) - } - ResponseHeader1xx = append(ResponseHeader1xx, data...) - if bytes.Equal(ResponseHeader1xx[len(ResponseHeader1xx)-4:], []byte{'\r', '\n', '\r', '\n'}) { - break - } - if len(ResponseHeader1xx) > 1024 { - return nil, errors.New("too big http 1xx response") - } - } - writer.Write(ResponseHeader1xx) - } - } - return http.ReadResponse(r, req) -} - func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) diff --git a/proxy/loopback/config.go b/proxy/loopback/config.go index 460b3070..95b1ae94 100644 --- a/proxy/loopback/config.go +++ b/proxy/loopback/config.go @@ -1 +1,3 @@ package loopback + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/proxy/loopback/config.pb.go b/proxy/loopback/config.pb.go index 29612918..c13c14af 100644 --- a/proxy/loopback/config.pb.go +++ b/proxy/loopback/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/loopback/config.proto package loopback @@ -30,9 +30,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_loopback_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_loopback_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -43,7 +45,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_loopback_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -96,7 +98,7 @@ func file_proxy_loopback_config_proto_rawDescGZIP() []byte { } var file_proxy_loopback_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_loopback_config_proto_goTypes = []any{ +var file_proxy_loopback_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.proxy.loopback.Config } var file_proxy_loopback_config_proto_depIdxs = []int32{ @@ -112,6 +114,20 @@ func file_proxy_loopback_config_proto_init() { if File_proxy_loopback_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_loopback_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/loopback/errors.generated.go b/proxy/loopback/errors.generated.go new file mode 100644 index 00000000..0db02932 --- /dev/null +++ b/proxy/loopback/errors.generated.go @@ -0,0 +1,9 @@ +package loopback + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/loopback/loopback.go b/proxy/loopback/loopback.go index e1cac3c6..946847f3 100644 --- a/proxy/loopback/loopback.go +++ b/proxy/loopback/loopback.go @@ -5,7 +5,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/retry" @@ -23,15 +22,13 @@ type Loopback struct { } func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified.") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified.") } - ob.Name = "loopback" - destination := ob.Target + destination := outbound.Target - errors.LogInfo(ctx, "opening connection to ", destination) + newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx)) input := link.Reader output := link.Writer @@ -67,7 +64,7 @@ func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet return nil }) if err != nil { - return errors.New("failed to open connection to ", destination).Base(err) + return newError("failed to open connection to ", destination).Base(err) } defer conn.Close() @@ -80,7 +77,7 @@ func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet } if err := buf.Copy(input, writer); err != nil { - return errors.New("failed to process request").Base(err) + return newError("failed to process request").Base(err) } return nil @@ -94,14 +91,14 @@ func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet reader = buf.NewPacketReader(conn) } if err := buf.Copy(reader, output); err != nil { - return errors.New("failed to process response").Base(err) + return newError("failed to process response").Base(err) } return nil } if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil diff --git a/proxy/mtproto/auth.go b/proxy/mtproto/auth.go new file mode 100644 index 00000000..cdd66d6f --- /dev/null +++ b/proxy/mtproto/auth.go @@ -0,0 +1,148 @@ +package mtproto + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "io" + "sync" + + "github.com/xtls/xray-core/common" +) + +const ( + HeaderSize = 64 +) + +type SessionContext struct { + ConnectionType [4]byte + DataCenterID uint16 +} + +func DefaultSessionContext() SessionContext { + return SessionContext{ + ConnectionType: [4]byte{0xef, 0xef, 0xef, 0xef}, + DataCenterID: 0, + } +} + +type contextKey int32 + +const ( + sessionContextKey contextKey = iota +) + +func ContextWithSessionContext(ctx context.Context, c SessionContext) context.Context { + return context.WithValue(ctx, sessionContextKey, c) +} + +func SessionContextFromContext(ctx context.Context) SessionContext { + if c := ctx.Value(sessionContextKey); c != nil { + return c.(SessionContext) + } + return DefaultSessionContext() +} + +type Authentication struct { + Header [HeaderSize]byte + DecodingKey [32]byte + EncodingKey [32]byte + DecodingNonce [16]byte + EncodingNonce [16]byte +} + +func (a *Authentication) DataCenterID() uint16 { + x := ((int16(a.Header[61]) << 8) | int16(a.Header[60])) + if x < 0 { + x = -x + } + return uint16(x) - 1 +} + +func (a *Authentication) ConnectionType() [4]byte { + var x [4]byte + copy(x[:], a.Header[56:60]) + return x +} + +func (a *Authentication) ApplySecret(b []byte) { + a.DecodingKey = sha256.Sum256(append(a.DecodingKey[:], b...)) + a.EncodingKey = sha256.Sum256(append(a.EncodingKey[:], b...)) +} + +func generateRandomBytes(random []byte, connType [4]byte) { + for { + common.Must2(rand.Read(random)) + + if random[0] == 0xef { + continue + } + + val := (uint32(random[3]) << 24) | (uint32(random[2]) << 16) | (uint32(random[1]) << 8) | uint32(random[0]) + if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee { + continue + } + + if (uint32(random[7])<<24)|(uint32(random[6])<<16)|(uint32(random[5])<<8)|uint32(random[4]) == 0x00000000 { + continue + } + + copy(random[56:60], connType[:]) + + return + } +} + +func NewAuthentication(sc SessionContext) *Authentication { + auth := getAuthenticationObject() + random := auth.Header[:] + generateRandomBytes(random, sc.ConnectionType) + copy(auth.EncodingKey[:], random[8:]) + copy(auth.EncodingNonce[:], random[8+32:]) + keyivInverse := Inverse(random[8 : 8+32+16]) + copy(auth.DecodingKey[:], keyivInverse) + copy(auth.DecodingNonce[:], keyivInverse[32:]) + return auth +} + +func ReadAuthentication(reader io.Reader) (*Authentication, error) { + auth := getAuthenticationObject() + + if _, err := io.ReadFull(reader, auth.Header[:]); err != nil { + putAuthenticationObject(auth) + return nil, err + } + + copy(auth.DecodingKey[:], auth.Header[8:]) + copy(auth.DecodingNonce[:], auth.Header[8+32:]) + keyivInverse := Inverse(auth.Header[8 : 8+32+16]) + copy(auth.EncodingKey[:], keyivInverse) + copy(auth.EncodingNonce[:], keyivInverse[32:]) + + return auth, nil +} + +// Inverse returns a new byte array. It is a sequence of bytes when the input is read from end to beginning.Inverse +// Visible for testing only. +func Inverse(b []byte) []byte { + lenb := len(b) + b2 := make([]byte, lenb) + for i, v := range b { + b2[lenb-i-1] = v + } + return b2 +} + +var authPool = sync.Pool{ + New: func() interface{} { + return new(Authentication) + }, +} + +func getAuthenticationObject() *Authentication { + return authPool.Get().(*Authentication) +} + +func putAuthenticationObject(auth *Authentication) { + authPool.Put(auth) +} diff --git a/proxy/mtproto/auth_test.go b/proxy/mtproto/auth_test.go new file mode 100644 index 00000000..a05bc743 --- /dev/null +++ b/proxy/mtproto/auth_test.go @@ -0,0 +1,52 @@ +package mtproto_test + +import ( + "bytes" + "crypto/rand" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/xtls/xray-core/common" + . "github.com/xtls/xray-core/proxy/mtproto" +) + +func TestInverse(t *testing.T) { + const size = 64 + b := make([]byte, 64) + for b[0] == b[size-1] { + common.Must2(rand.Read(b)) + } + + bi := Inverse(b) + if b[0] == bi[0] { + t.Fatal("seems bytes are not inversed: ", b[0], "vs", bi[0]) + } + + bii := Inverse(bi) + if r := cmp.Diff(bii, b); r != "" { + t.Fatal(r) + } +} + +func TestAuthenticationReadWrite(t *testing.T) { + a := NewAuthentication(DefaultSessionContext()) + b := bytes.NewReader(a.Header[:]) + a2, err := ReadAuthentication(b) + common.Must(err) + + if r := cmp.Diff(a.EncodingKey[:], a2.DecodingKey[:]); r != "" { + t.Error("decoding key: ", r) + } + + if r := cmp.Diff(a.EncodingNonce[:], a2.DecodingNonce[:]); r != "" { + t.Error("decoding nonce: ", r) + } + + if r := cmp.Diff(a.DecodingKey[:], a2.EncodingKey[:]); r != "" { + t.Error("encoding key: ", r) + } + + if r := cmp.Diff(a.DecodingNonce[:], a2.EncodingNonce[:]); r != "" { + t.Error("encoding nonce: ", r) + } +} diff --git a/proxy/mtproto/client.go b/proxy/mtproto/client.go new file mode 100644 index 00000000..6825fdae --- /dev/null +++ b/proxy/mtproto/client.go @@ -0,0 +1,76 @@ +package mtproto + +import ( + "context" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/crypto" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/task" + "github.com/xtls/xray-core/transport" + "github.com/xtls/xray-core/transport/internet" +) + +type Client struct{} + +func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { + return &Client{}, nil +} + +func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("unknown destination.") + } + dest := outbound.Target + if dest.Network != net.Network_TCP { + return newError("not TCP traffic", dest) + } + + conn, err := dialer.Dial(ctx, dest) + if err != nil { + return newError("failed to dial to ", dest).Base(err).AtWarning() + } + defer conn.Close() + + sc := SessionContextFromContext(ctx) + auth := NewAuthentication(sc) + defer putAuthenticationObject(auth) + + request := func() error { + encryptor := crypto.NewAesCTRStream(auth.EncodingKey[:], auth.EncodingNonce[:]) + + var header [HeaderSize]byte + encryptor.XORKeyStream(header[:], auth.Header[:]) + copy(header[:56], auth.Header[:]) + + if _, err := conn.Write(header[:]); err != nil { + return newError("failed to write auth header").Base(err) + } + + connWriter := buf.NewWriter(crypto.NewCryptionWriter(encryptor, conn)) + return buf.Copy(link.Reader, connWriter) + } + + response := func() error { + decryptor := crypto.NewAesCTRStream(auth.DecodingKey[:], auth.DecodingNonce[:]) + + connReader := buf.NewReader(crypto.NewCryptionReader(decryptor, conn)) + return buf.Copy(connReader, link.Writer) + } + + responseDoneAndCloseWriter := task.OnSuccess(response, task.Close(link.Writer)) + if err := task.Run(ctx, request, responseDoneAndCloseWriter); err != nil { + return newError("connection ends").Base(err) + } + + return nil +} + +func init() { + common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewClient(ctx, config.(*ClientConfig)) + })) +} diff --git a/proxy/mtproto/config.go b/proxy/mtproto/config.go new file mode 100644 index 00000000..fcd203d7 --- /dev/null +++ b/proxy/mtproto/config.go @@ -0,0 +1,24 @@ +package mtproto + +import ( + "github.com/xtls/xray-core/common/protocol" +) + +func (a *Account) Equals(another protocol.Account) bool { + aa, ok := another.(*Account) + if !ok { + return false + } + + if len(a.Secret) != len(aa.Secret) { + return false + } + + for i, v := range a.Secret { + if v != aa.Secret[i] { + return false + } + } + + return true +} diff --git a/proxy/mtproto/config.pb.go b/proxy/mtproto/config.pb.go new file mode 100644 index 00000000..425c7672 --- /dev/null +++ b/proxy/mtproto/config.pb.go @@ -0,0 +1,272 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: proxy/mtproto/config.proto + +package mtproto + +import ( + protocol "github.com/xtls/xray-core/common/protocol" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Account struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Secret []byte `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` +} + +func (x *Account) Reset() { + *x = Account{} + if protoimpl.UnsafeEnabled { + mi := &file_proxy_mtproto_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Account) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Account) ProtoMessage() {} + +func (x *Account) ProtoReflect() protoreflect.Message { + mi := &file_proxy_mtproto_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Account.ProtoReflect.Descriptor instead. +func (*Account) Descriptor() ([]byte, []int) { + return file_proxy_mtproto_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Account) GetSecret() []byte { + if x != nil { + return x.Secret + } + return nil +} + +type ServerConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // User is a list of users that allowed to connect to this inbound. + // Although this is a repeated field, only the first user is effective for + // now. + User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` +} + +func (x *ServerConfig) Reset() { + *x = ServerConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_proxy_mtproto_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerConfig) ProtoMessage() {} + +func (x *ServerConfig) ProtoReflect() protoreflect.Message { + mi := &file_proxy_mtproto_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. +func (*ServerConfig) Descriptor() ([]byte, []int) { + return file_proxy_mtproto_config_proto_rawDescGZIP(), []int{1} +} + +func (x *ServerConfig) GetUser() []*protocol.User { + if x != nil { + return x.User + } + return nil +} + +type ClientConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ClientConfig) Reset() { + *x = ClientConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_proxy_mtproto_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientConfig) ProtoMessage() {} + +func (x *ClientConfig) ProtoReflect() protoreflect.Message { + mi := &file_proxy_mtproto_config_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. +func (*ClientConfig) Descriptor() ([]byte, []int) { + return file_proxy_mtproto_config_proto_rawDescGZIP(), []int{2} +} + +var File_proxy_mtproto_config_proto protoreflect.FileDescriptor + +var file_proxy_mtproto_config_proto_rawDesc = []byte{ + 0x0a, 0x1a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x6d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x78, 0x72, + 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x6d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x21, 0x0a, 0x07, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, + 0x3e, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, + 0x0e, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, + 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x6d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x6d, 0x74, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x4d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_proxy_mtproto_config_proto_rawDescOnce sync.Once + file_proxy_mtproto_config_proto_rawDescData = file_proxy_mtproto_config_proto_rawDesc +) + +func file_proxy_mtproto_config_proto_rawDescGZIP() []byte { + file_proxy_mtproto_config_proto_rawDescOnce.Do(func() { + file_proxy_mtproto_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_mtproto_config_proto_rawDescData) + }) + return file_proxy_mtproto_config_proto_rawDescData +} + +var file_proxy_mtproto_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_proxy_mtproto_config_proto_goTypes = []interface{}{ + (*Account)(nil), // 0: xray.proxy.mtproto.Account + (*ServerConfig)(nil), // 1: xray.proxy.mtproto.ServerConfig + (*ClientConfig)(nil), // 2: xray.proxy.mtproto.ClientConfig + (*protocol.User)(nil), // 3: xray.common.protocol.User +} +var file_proxy_mtproto_config_proto_depIdxs = []int32{ + 3, // 0: xray.proxy.mtproto.ServerConfig.user:type_name -> xray.common.protocol.User + 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_proxy_mtproto_config_proto_init() } +func file_proxy_mtproto_config_proto_init() { + if File_proxy_mtproto_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proxy_mtproto_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_mtproto_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_mtproto_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proxy_mtproto_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proxy_mtproto_config_proto_goTypes, + DependencyIndexes: file_proxy_mtproto_config_proto_depIdxs, + MessageInfos: file_proxy_mtproto_config_proto_msgTypes, + }.Build() + File_proxy_mtproto_config_proto = out.File + file_proxy_mtproto_config_proto_rawDesc = nil + file_proxy_mtproto_config_proto_goTypes = nil + file_proxy_mtproto_config_proto_depIdxs = nil +} diff --git a/proxy/mtproto/config.proto b/proxy/mtproto/config.proto new file mode 100644 index 00000000..65997bc0 --- /dev/null +++ b/proxy/mtproto/config.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package xray.proxy.mtproto; +option csharp_namespace = "Xray.Proxy.Mtproto"; +option go_package = "github.com/xtls/xray-core/proxy/mtproto"; +option java_package = "com.xray.proxy.mtproto"; +option java_multiple_files = true; + +import "common/protocol/user.proto"; + +message Account { + bytes secret = 1; +} + +message ServerConfig { + // User is a list of users that allowed to connect to this inbound. + // Although this is a repeated field, only the first user is effective for + // now. + repeated xray.common.protocol.User user = 1; +} + +message ClientConfig {} diff --git a/proxy/mtproto/errors.generated.go b/proxy/mtproto/errors.generated.go new file mode 100644 index 00000000..012202d6 --- /dev/null +++ b/proxy/mtproto/errors.generated.go @@ -0,0 +1,9 @@ +package mtproto + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/mtproto/mtproto.go b/proxy/mtproto/mtproto.go new file mode 100644 index 00000000..af7983a5 --- /dev/null +++ b/proxy/mtproto/mtproto.go @@ -0,0 +1,3 @@ +package mtproto + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/proxy/mtproto/server.go b/proxy/mtproto/server.go new file mode 100644 index 00000000..2079df1f --- /dev/null +++ b/proxy/mtproto/server.go @@ -0,0 +1,160 @@ +package mtproto + +import ( + "bytes" + "context" + "time" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/crypto" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/protocol" + "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/core" + "github.com/xtls/xray-core/features/policy" + "github.com/xtls/xray-core/features/routing" + "github.com/xtls/xray-core/transport/internet/stat" +) + +var dcList = []net.Address{ + net.ParseAddress("149.154.175.50"), + net.ParseAddress("149.154.167.51"), + net.ParseAddress("149.154.175.100"), + net.ParseAddress("149.154.167.91"), + net.ParseAddress("149.154.171.5"), +} + +type Server struct { + user *protocol.User + account *Account + policy policy.Manager +} + +func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { + if len(config.User) == 0 { + return nil, newError("no user configured.") + } + + user := config.User[0] + rawAccount, err := config.User[0].GetTypedAccount() + if err != nil { + return nil, newError("invalid account").Base(err) + } + account, ok := rawAccount.(*Account) + if !ok { + return nil, newError("not a MTProto account") + } + + v := core.MustFromContext(ctx) + + return &Server{ + user: user, + account: account, + policy: v.GetFeature(policy.ManagerType()).(policy.Manager), + }, nil +} + +func (s *Server) Network() []net.Network { + return []net.Network{net.Network_TCP} +} + +var ( + ctype1 = []byte{0xef, 0xef, 0xef, 0xef} + ctype2 = []byte{0xee, 0xee, 0xee, 0xee} +) + +func isValidConnectionType(c [4]byte) bool { + if bytes.Equal(c[:], ctype1) { + return true + } + if bytes.Equal(c[:], ctype2) { + return true + } + return false +} + +func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { + sPolicy := s.policy.ForLevel(s.user.Level) + + if err := conn.SetDeadline(time.Now().Add(sPolicy.Timeouts.Handshake)); err != nil { + newError("failed to set deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + auth, err := ReadAuthentication(conn) + if err != nil { + return newError("failed to read authentication header").Base(err) + } + defer putAuthenticationObject(auth) + + if err := conn.SetDeadline(time.Time{}); err != nil { + newError("failed to clear deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + + auth.ApplySecret(s.account.Secret) + + decryptor := crypto.NewAesCTRStream(auth.DecodingKey[:], auth.DecodingNonce[:]) + decryptor.XORKeyStream(auth.Header[:], auth.Header[:]) + + ct := auth.ConnectionType() + if !isValidConnectionType(ct) { + return newError("invalid connection type: ", ct) + } + + dcID := auth.DataCenterID() + if dcID >= uint16(len(dcList)) { + return newError("invalid datacenter id: ", dcID) + } + + dest := net.Destination{ + Network: net.Network_TCP, + Address: dcList[dcID], + Port: net.Port(443), + } + + ctx, cancel := context.WithCancel(ctx) + timer := signal.CancelAfterInactivity(ctx, cancel, sPolicy.Timeouts.ConnectionIdle) + ctx = policy.ContextWithBufferPolicy(ctx, sPolicy.Buffer) + + sc := SessionContext{ + ConnectionType: ct, + DataCenterID: dcID, + } + ctx = ContextWithSessionContext(ctx, sc) + + link, err := dispatcher.Dispatch(ctx, dest) + if err != nil { + return newError("failed to dispatch request to: ", dest).Base(err) + } + + request := func() error { + defer timer.SetTimeout(sPolicy.Timeouts.DownlinkOnly) + + reader := buf.NewReader(crypto.NewCryptionReader(decryptor, conn)) + return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)) + } + + response := func() error { + defer timer.SetTimeout(sPolicy.Timeouts.UplinkOnly) + + encryptor := crypto.NewAesCTRStream(auth.EncodingKey[:], auth.EncodingNonce[:]) + writer := buf.NewWriter(crypto.NewCryptionWriter(encryptor, conn)) + return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)) + } + + responseDoneAndCloseWriter := task.OnSuccess(response, task.Close(link.Writer)) + if err := task.Run(ctx, request, responseDoneAndCloseWriter); err != nil { + common.Interrupt(link.Reader) + common.Interrupt(link.Writer) + return newError("connection ends").Base(err) + } + + return nil +} + +func init() { + common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewServer(ctx, config.(*ServerConfig)) + })) +} diff --git a/proxy/proxy.go b/proxy/proxy.go index 3fec31af..fb52605c 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -6,54 +6,14 @@ package proxy import ( - "bytes" "context" - "crypto/rand" - "io" - "math/big" - "runtime" - "strconv" - "time" - "github.com/pires/go-proxyproto" - "github.com/xtls/xray-core/app/dispatcher" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" - "github.com/xtls/xray-core/transport/internet/tls" -) - -var ( - Tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04} - TlsClientHandShakeStart = []byte{0x16, 0x03} - TlsServerHandShakeStart = []byte{0x16, 0x03, 0x03} - TlsApplicationDataStart = []byte{0x17, 0x03, 0x03} - - Tls13CipherSuiteDic = map[uint16]string{ - 0x1301: "TLS_AES_128_GCM_SHA256", - 0x1302: "TLS_AES_256_GCM_SHA384", - 0x1303: "TLS_CHACHA20_POLY1305_SHA256", - 0x1304: "TLS_AES_128_CCM_SHA256", - 0x1305: "TLS_AES_128_CCM_8_SHA256", - } -) - -const ( - TlsHandshakeTypeClientHello byte = 0x01 - TlsHandshakeTypeServerHello byte = 0x02 - - CommandPaddingContinue byte = 0x00 - CommandPaddingEnd byte = 0x01 - CommandPaddingDirect byte = 0x02 ) // An Inbound processes inbound connections. @@ -78,15 +38,6 @@ type UserManager interface { // RemoveUser removes a user by email. RemoveUser(context.Context, string) error - - // Get user by email. - GetUser(context.Context, string) *protocol.MemoryUser - - // Get all users. - GetUsers(context.Context) []*protocol.MemoryUser - - // Get users count. - GetUsersCount(context.Context) int64 } type GetInbound interface { @@ -96,545 +47,3 @@ type GetInbound interface { type GetOutbound interface { GetOutbound() Outbound } - -// TrafficState is used to track uplink and downlink of one connection -// It is used by XTLS to determine if switch to raw copy mode, It is used by Vision to calculate padding -type TrafficState struct { - UserUUID []byte - NumberOfPacketToFilter int - EnableXtls bool - IsTLS12orAbove bool - IsTLS bool - Cipher uint16 - RemainingServerHello int32 - Inbound InboundState - Outbound OutboundState -} - -type InboundState struct { - // reader link state - WithinPaddingBuffers bool - UplinkReaderDirectCopy bool - RemainingCommand int32 - RemainingContent int32 - RemainingPadding int32 - CurrentCommand int - // write link state - IsPadding bool - DownlinkWriterDirectCopy bool -} - -type OutboundState struct { - // reader link state - WithinPaddingBuffers bool - DownlinkReaderDirectCopy bool - RemainingCommand int32 - RemainingContent int32 - RemainingPadding int32 - CurrentCommand int - // write link state - IsPadding bool - UplinkWriterDirectCopy bool -} - -func NewTrafficState(userUUID []byte) *TrafficState { - return &TrafficState{ - UserUUID: userUUID, - NumberOfPacketToFilter: 8, - EnableXtls: false, - IsTLS12orAbove: false, - IsTLS: false, - Cipher: 0, - RemainingServerHello: -1, - Inbound: InboundState{ - WithinPaddingBuffers: true, - UplinkReaderDirectCopy: false, - RemainingCommand: -1, - RemainingContent: -1, - RemainingPadding: -1, - CurrentCommand: 0, - IsPadding: true, - DownlinkWriterDirectCopy: false, - }, - Outbound: OutboundState{ - WithinPaddingBuffers: true, - DownlinkReaderDirectCopy: false, - RemainingCommand: -1, - RemainingContent: -1, - RemainingPadding: -1, - CurrentCommand: 0, - IsPadding: true, - UplinkWriterDirectCopy: false, - }, - } -} - -// VisionReader is used to read xtls vision protocol -// Note Vision probably only make sense as the inner most layer of reader, since it need assess traffic state from origin proxy traffic -type VisionReader struct { - buf.Reader - trafficState *TrafficState - ctx context.Context - isUplink bool -} - -func NewVisionReader(reader buf.Reader, state *TrafficState, isUplink bool, context context.Context) *VisionReader { - return &VisionReader{ - Reader: reader, - trafficState: state, - ctx: context, - isUplink: isUplink, - } -} - -func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) { - buffer, err := w.Reader.ReadMultiBuffer() - if !buffer.IsEmpty() { - var withinPaddingBuffers *bool - var remainingContent *int32 - var remainingPadding *int32 - var currentCommand *int - var switchToDirectCopy *bool - if w.isUplink { - withinPaddingBuffers = &w.trafficState.Inbound.WithinPaddingBuffers - remainingContent = &w.trafficState.Inbound.RemainingContent - remainingPadding = &w.trafficState.Inbound.RemainingPadding - currentCommand = &w.trafficState.Inbound.CurrentCommand - switchToDirectCopy = &w.trafficState.Inbound.UplinkReaderDirectCopy - } else { - withinPaddingBuffers = &w.trafficState.Outbound.WithinPaddingBuffers - remainingContent = &w.trafficState.Outbound.RemainingContent - remainingPadding = &w.trafficState.Outbound.RemainingPadding - currentCommand = &w.trafficState.Outbound.CurrentCommand - switchToDirectCopy = &w.trafficState.Outbound.DownlinkReaderDirectCopy - } - - if *withinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 { - mb2 := make(buf.MultiBuffer, 0, len(buffer)) - for _, b := range buffer { - newbuffer := XtlsUnpadding(b, w.trafficState, w.isUplink, w.ctx) - if newbuffer.Len() > 0 { - mb2 = append(mb2, newbuffer) - } - } - buffer = mb2 - if *remainingContent > 0 || *remainingPadding > 0 || *currentCommand == 0 { - *withinPaddingBuffers = true - } else if *currentCommand == 1 { - *withinPaddingBuffers = false - } else if *currentCommand == 2 { - *withinPaddingBuffers = false - *switchToDirectCopy = true - } else { - errors.LogInfo(w.ctx, "XtlsRead unknown command ", *currentCommand, buffer.Len()) - } - } - if w.trafficState.NumberOfPacketToFilter > 0 { - XtlsFilterTls(buffer, w.trafficState, w.ctx) - } - } - return buffer, err -} - -// VisionWriter is used to write xtls vision protocol -// Note Vision probably only make sense as the inner most layer of writer, since it need assess traffic state from origin proxy traffic -type VisionWriter struct { - buf.Writer - trafficState *TrafficState - ctx context.Context - writeOnceUserUUID []byte - isUplink bool -} - -func NewVisionWriter(writer buf.Writer, state *TrafficState, isUplink bool, context context.Context) *VisionWriter { - w := make([]byte, len(state.UserUUID)) - copy(w, state.UserUUID) - return &VisionWriter{ - Writer: writer, - trafficState: state, - ctx: context, - writeOnceUserUUID: w, - isUplink: isUplink, - } -} - -func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { - if w.trafficState.NumberOfPacketToFilter > 0 { - XtlsFilterTls(mb, w.trafficState, w.ctx) - } - var isPadding *bool - var switchToDirectCopy *bool - if w.isUplink { - isPadding = &w.trafficState.Outbound.IsPadding - switchToDirectCopy = &w.trafficState.Outbound.UplinkWriterDirectCopy - } else { - isPadding = &w.trafficState.Inbound.IsPadding - switchToDirectCopy = &w.trafficState.Inbound.DownlinkWriterDirectCopy - } - if *isPadding { - if len(mb) == 1 && mb[0] == nil { - mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header - return w.Writer.WriteMultiBuffer(mb) - } - mb = ReshapeMultiBuffer(w.ctx, mb) - longPadding := w.trafficState.IsTLS - for i, b := range mb { - if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) { - if w.trafficState.EnableXtls { - *switchToDirectCopy = true - } - var command byte = CommandPaddingContinue - if i == len(mb)-1 { - command = CommandPaddingEnd - if w.trafficState.EnableXtls { - command = CommandPaddingDirect - } - } - mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx) - *isPadding = false // padding going to end - longPadding = false - continue - } else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early - *isPadding = false - mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx) - break - } - var command byte = CommandPaddingContinue - if i == len(mb)-1 && !*isPadding { - command = CommandPaddingEnd - if w.trafficState.EnableXtls { - command = CommandPaddingDirect - } - } - mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx) - } - } - return w.Writer.WriteMultiBuffer(mb) -} - -// ReshapeMultiBuffer prepare multi buffer for padding structure (max 21 bytes) -func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBuffer { - needReshape := 0 - for _, b := range buffer { - if b.Len() >= buf.Size-21 { - needReshape += 1 - } - } - if needReshape == 0 { - return buffer - } - mb2 := make(buf.MultiBuffer, 0, len(buffer)+needReshape) - toPrint := "" - for i, buffer1 := range buffer { - if buffer1.Len() >= buf.Size-21 { - index := int32(bytes.LastIndex(buffer1.Bytes(), TlsApplicationDataStart)) - if index < 21 || index > buf.Size-21 { - index = buf.Size / 2 - } - buffer2 := buf.New() - buffer2.Write(buffer1.BytesFrom(index)) - buffer1.Resize(0, index) - mb2 = append(mb2, buffer1, buffer2) - toPrint += " " + strconv.Itoa(int(buffer1.Len())) + " " + strconv.Itoa(int(buffer2.Len())) - } else { - mb2 = append(mb2, buffer1) - toPrint += " " + strconv.Itoa(int(buffer1.Len())) - } - buffer[i] = nil - } - buffer = buffer[:0] - errors.LogInfo(ctx, "ReshapeMultiBuffer ", toPrint) - return mb2 -} - -// XtlsPadding add padding to eliminate length signature during tls handshake -func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer { - var contentLen int32 = 0 - var paddingLen int32 = 0 - if b != nil { - contentLen = b.Len() - } - if contentLen < 900 && longPadding { - l, err := rand.Int(rand.Reader, big.NewInt(500)) - if err != nil { - errors.LogDebugInner(ctx, err, "failed to generate padding") - } - paddingLen = int32(l.Int64()) + 900 - contentLen - } else { - l, err := rand.Int(rand.Reader, big.NewInt(256)) - if err != nil { - errors.LogDebugInner(ctx, err, "failed to generate padding") - } - paddingLen = int32(l.Int64()) - } - if paddingLen > buf.Size-21-contentLen { - paddingLen = buf.Size - 21 - contentLen - } - newbuffer := buf.New() - if userUUID != nil { - newbuffer.Write(*userUUID) - *userUUID = nil - } - newbuffer.Write([]byte{command, byte(contentLen >> 8), byte(contentLen), byte(paddingLen >> 8), byte(paddingLen)}) - if b != nil { - newbuffer.Write(b.Bytes()) - b.Release() - b = nil - } - newbuffer.Extend(paddingLen) - errors.LogInfo(ctx, "XtlsPadding ", contentLen, " ", paddingLen, " ", command) - return newbuffer -} - -// XtlsUnpadding remove padding and parse command -func XtlsUnpadding(b *buf.Buffer, s *TrafficState, isUplink bool, ctx context.Context) *buf.Buffer { - var remainingCommand *int32 - var remainingContent *int32 - var remainingPadding *int32 - var currentCommand *int - if isUplink { - remainingCommand = &s.Inbound.RemainingCommand - remainingContent = &s.Inbound.RemainingContent - remainingPadding = &s.Inbound.RemainingPadding - currentCommand = &s.Inbound.CurrentCommand - } else { - remainingCommand = &s.Outbound.RemainingCommand - remainingContent = &s.Outbound.RemainingContent - remainingPadding = &s.Outbound.RemainingPadding - currentCommand = &s.Outbound.CurrentCommand - } - if *remainingCommand == -1 && *remainingContent == -1 && *remainingPadding == -1 { // initial state - if b.Len() >= 21 && bytes.Equal(s.UserUUID, b.BytesTo(16)) { - b.Advance(16) - *remainingCommand = 5 - } else { - return b - } - } - newbuffer := buf.New() - for b.Len() > 0 { - if *remainingCommand > 0 { - data, err := b.ReadByte() - if err != nil { - return newbuffer - } - switch *remainingCommand { - case 5: - *currentCommand = int(data) - case 4: - *remainingContent = int32(data) << 8 - case 3: - *remainingContent = *remainingContent | int32(data) - case 2: - *remainingPadding = int32(data) << 8 - case 1: - *remainingPadding = *remainingPadding | int32(data) - errors.LogInfo(ctx, "Xtls Unpadding new block, content ", *remainingContent, " padding ", *remainingPadding, " command ", *currentCommand) - } - *remainingCommand-- - } else if *remainingContent > 0 { - len := *remainingContent - if b.Len() < len { - len = b.Len() - } - data, err := b.ReadBytes(len) - if err != nil { - return newbuffer - } - newbuffer.Write(data) - *remainingContent -= len - } else { // remainingPadding > 0 - len := *remainingPadding - if b.Len() < len { - len = b.Len() - } - b.Advance(len) - *remainingPadding -= len - } - if *remainingCommand <= 0 && *remainingContent <= 0 && *remainingPadding <= 0 { // this block done - if *currentCommand == 0 { - *remainingCommand = 5 - } else { - *remainingCommand = -1 // set to initial state - *remainingContent = -1 - *remainingPadding = -1 - if b.Len() > 0 { // shouldn't happen - newbuffer.Write(b.Bytes()) - } - break - } - } - } - b.Release() - b = nil - return newbuffer -} - -// XtlsFilterTls filter and recognize tls 1.3 and other info -func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx context.Context) { - for _, b := range buffer { - if b == nil { - continue - } - trafficState.NumberOfPacketToFilter-- - if b.Len() >= 6 { - startsBytes := b.BytesTo(6) - if bytes.Equal(TlsServerHandShakeStart, startsBytes[:3]) && startsBytes[5] == TlsHandshakeTypeServerHello { - trafficState.RemainingServerHello = (int32(startsBytes[3])<<8 | int32(startsBytes[4])) + 5 - trafficState.IsTLS12orAbove = true - trafficState.IsTLS = true - if b.Len() >= 79 && trafficState.RemainingServerHello >= 79 { - sessionIdLen := int32(b.Byte(43)) - cipherSuite := b.BytesRange(43+sessionIdLen+1, 43+sessionIdLen+3) - trafficState.Cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1]) - } else { - errors.LogInfo(ctx, "XtlsFilterTls short server hello, tls 1.2 or older? ", b.Len(), " ", trafficState.RemainingServerHello) - } - } else if bytes.Equal(TlsClientHandShakeStart, startsBytes[:2]) && startsBytes[5] == TlsHandshakeTypeClientHello { - trafficState.IsTLS = true - errors.LogInfo(ctx, "XtlsFilterTls found tls client hello! ", buffer.Len()) - } - } - if trafficState.RemainingServerHello > 0 { - end := trafficState.RemainingServerHello - if end > b.Len() { - end = b.Len() - } - trafficState.RemainingServerHello -= b.Len() - if bytes.Contains(b.BytesTo(end), Tls13SupportedVersions) { - v, ok := Tls13CipherSuiteDic[trafficState.Cipher] - if !ok { - v = "Old cipher: " + strconv.FormatUint(uint64(trafficState.Cipher), 16) - } else if v != "TLS_AES_128_CCM_8_SHA256" { - trafficState.EnableXtls = true - } - errors.LogInfo(ctx, "XtlsFilterTls found tls 1.3! ", b.Len(), " ", v) - trafficState.NumberOfPacketToFilter = 0 - return - } else if trafficState.RemainingServerHello <= 0 { - errors.LogInfo(ctx, "XtlsFilterTls found tls 1.2! ", b.Len()) - trafficState.NumberOfPacketToFilter = 0 - return - } - errors.LogInfo(ctx, "XtlsFilterTls inconclusive server hello ", b.Len(), " ", trafficState.RemainingServerHello) - } - if trafficState.NumberOfPacketToFilter <= 0 { - errors.LogInfo(ctx, "XtlsFilterTls stop filtering", buffer.Len()) - } - } -} - -// UnwrapRawConn support unwrap stats, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it -func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { - var readCounter, writerCounter stats.Counter - if conn != nil { - statConn, ok := conn.(*stat.CounterConnection) - if ok { - conn = statConn.Connection - readCounter = statConn.ReadCounter - writerCounter = statConn.WriteCounter - } - if xc, ok := conn.(*tls.Conn); ok { - conn = xc.NetConn() - } else if utlsConn, ok := conn.(*tls.UConn); ok { - conn = utlsConn.NetConn() - } else if realityConn, ok := conn.(*reality.Conn); ok { - conn = realityConn.NetConn() - } else if realityUConn, ok := conn.(*reality.UConn); ok { - conn = realityUConn.NetConn() - } - if pc, ok := conn.(*proxyproto.Conn); ok { - conn = pc.Raw() - // 8192 > 4096, there is no need to process pc's bufReader - } - if uc, ok := conn.(*internet.UnixConnWrapper); ok { - conn = uc.UnixConn - } - } - return conn, readCounter, writerCounter -} - -// CopyRawConnIfExist use the most efficient copy method. -// - If caller don't want to turn on splice, do not pass in both reader conn and writer conn -// - writer are from *transport.Link -func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net.Conn, writer buf.Writer, timer *signal.ActivityTimer, inTimer *signal.ActivityTimer) error { - readerConn, readCounter, _ := UnwrapRawConn(readerConn) - writerConn, _, writeCounter := UnwrapRawConn(writerConn) - reader := buf.NewReader(readerConn) - if runtime.GOOS != "linux" && runtime.GOOS != "android" { - return readV(ctx, reader, writer, timer, readCounter) - } - tc, ok := writerConn.(*net.TCPConn) - if !ok || readerConn == nil || writerConn == nil { - return readV(ctx, reader, writer, timer, readCounter) - } - inbound := session.InboundFromContext(ctx) - if inbound == nil || inbound.CanSpliceCopy == 3 { - return readV(ctx, reader, writer, timer, readCounter) - } - outbounds := session.OutboundsFromContext(ctx) - if len(outbounds) == 0 { - return readV(ctx, reader, writer, timer, readCounter) - } - for _, ob := range outbounds { - if ob.CanSpliceCopy == 3 { - return readV(ctx, reader, writer, timer, readCounter) - } - } - - for { - inbound := session.InboundFromContext(ctx) - outbounds := session.OutboundsFromContext(ctx) - var splice = inbound.CanSpliceCopy == 1 - for _, ob := range outbounds { - if ob.CanSpliceCopy != 1 { - splice = false - } - } - if splice { - errors.LogInfo(ctx, "CopyRawConn splice") - statWriter, _ := writer.(*dispatcher.SizeStatWriter) - //runtime.Gosched() // necessary - time.Sleep(time.Millisecond) // without this, there will be a rare ssl error for freedom splice - timer.SetTimeout(8 * time.Hour) // prevent leak, just in case - if inTimer != nil { - inTimer.SetTimeout(8 * time.Hour) - } - w, err := tc.ReadFrom(readerConn) - if readCounter != nil { - readCounter.Add(w) // outbound stats - } - if writeCounter != nil { - writeCounter.Add(w) // inbound stats - } - if statWriter != nil { - statWriter.Counter.Add(w) // user stats - } - if err != nil && errors.Cause(err) != io.EOF { - return err - } - return nil - } - buffer, err := reader.ReadMultiBuffer() - if !buffer.IsEmpty() { - if readCounter != nil { - readCounter.Add(int64(buffer.Len())) - } - timer.Update() - if werr := writer.WriteMultiBuffer(buffer); werr != nil { - return werr - } - } - if err != nil { - return err - } - } -} - -func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, readCounter stats.Counter) error { - errors.LogInfo(ctx, "CopyRawConn readv") - if err := buf.Copy(reader, writer, buf.UpdateActivity(timer), buf.AddToStatCounter(readCounter)); err != nil { - return errors.New("failed to process response").Base(err) - } - return nil -} diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index c2ad1c1c..2d8a4e81 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -6,7 +6,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/retry" @@ -32,12 +31,12 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { - return nil, errors.New("failed to parse server spec").Base(err) + return nil, newError("failed to parse server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { - return nil, errors.New("0 server") + return nil, newError("0 server") } v := core.MustFromContext(ctx) @@ -50,14 +49,11 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { // Process implements OutboundHandler.Process(). func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified") } - ob.Name = "shadowsocks" - ob.CanSpliceCopy = 3 - destination := ob.Target + destination := outbound.Target network := destination.Network var server *protocol.ServerSpec @@ -76,9 +72,9 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return nil }) if err != nil { - return errors.New("failed to find an available destination").AtWarning().Base(err) + return newError("failed to find an available destination").AtWarning().Base(err) } - errors.LogInfo(ctx, "tunneling request to ", destination, " via ", network, ":", server.Destination().NetAddr()) + newError("tunneling request to ", destination, " via ", network, ":", server.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx)) defer conn.Close() @@ -96,28 +92,13 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter user := server.PickUser() _, ok := user.Account.(*MemoryAccount) if !ok { - return errors.New("user account is not valid") + return newError("user account is not valid") } request.User = user - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) - } - sessionPolicy := c.policyManager.ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, sessionPolicy.Timeouts.ConnectionIdle) - - if newCtx != nil { - ctx = newCtx - } + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) if request.Command == protocol.RequestCommandTCP { requestDone := func() error { @@ -125,11 +106,11 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) bodyWriter, err := WriteTCPRequest(request, bufferedWriter) if err != nil { - return errors.New("failed to write request").Base(err) + return newError("failed to write request").Base(err) } if err = buf.CopyOnceTimeout(link.Reader, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { - return errors.New("failed to write A request payload").Base(err).AtWarning() + return newError("failed to write A request payload").Base(err).AtWarning() } if err := bufferedWriter.SetBuffered(false); err != nil { @@ -152,7 +133,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil @@ -169,7 +150,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter } if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all UDP request").Base(err) + return newError("failed to transport all UDP request").Base(err) } return nil } @@ -183,14 +164,14 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter } if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all UDP response").Base(err) + return newError("failed to transport all UDP response").Base(err) } return nil } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index a6d2ef87..1b977ded 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -6,14 +6,12 @@ import ( "crypto/cipher" "crypto/md5" "crypto/sha1" - "google.golang.org/protobuf/proto" "io" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/antireplay" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/crypto" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" @@ -21,15 +19,13 @@ import ( // MemoryAccount is an account type converted from Account. type MemoryAccount struct { - Cipher Cipher - CipherType CipherType - Key []byte - Password string + Cipher Cipher + Key []byte replayFilter antireplay.GeneralizedReplayFilter } -var ErrIVNotUnique = errors.New("IV is not unique") +var ErrIVNotUnique = newError("IV is not unique") // Equals implements protocol.Account.Equals(). func (a *MemoryAccount) Equals(another protocol.Account) bool { @@ -39,14 +35,6 @@ func (a *MemoryAccount) Equals(another protocol.Account) bool { return false } -func (a *MemoryAccount) ToProto() proto.Message { - return &Account{ - CipherType: a.CipherType, - Password: a.Password, - IvCheck: a.replayFilter != nil, - } -} - func (a *MemoryAccount) CheckIV(iv []byte) error { if a.replayFilter == nil { return nil @@ -106,7 +94,7 @@ func (a *Account) getCipher() (Cipher, error) { case CipherType_NONE: return NoneCipher{}, nil default: - return nil, errors.New("Unsupported cipher.") + return nil, newError("Unsupported cipher.") } } @@ -114,13 +102,11 @@ func (a *Account) getCipher() (Cipher, error) { func (a *Account) AsAccount() (protocol.Account, error) { Cipher, err := a.getCipher() if err != nil { - return nil, errors.New("failed to get cipher").Base(err) + return nil, newError("failed to get cipher").Base(err) } return &MemoryAccount{ - Cipher: Cipher, - CipherType: a.CipherType, - Key: passwordToCipherKey([]byte(a.Password), Cipher.KeySize()), - Password: a.Password, + Cipher: Cipher, + Key: passwordToCipherKey([]byte(a.Password), Cipher.KeySize()), replayFilter: func() antireplay.GeneralizedReplayFilter { if a.IvCheck { return antireplay.NewBloomRing() @@ -196,7 +182,7 @@ func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error { func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error { if b.Len() <= c.IVSize() { - return errors.New("insufficient data: ", b.Len()) + return newError("insufficient data: ", b.Len()) } ivLen := c.IVSize() payloadLen := b.Len() diff --git a/proxy/shadowsocks/config.pb.go b/proxy/shadowsocks/config.pb.go index 56cddb67..5e89e40f 100644 --- a/proxy/shadowsocks/config.pb.go +++ b/proxy/shadowsocks/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/shadowsocks/config.proto package shadowsocks @@ -92,9 +92,11 @@ type Account struct { func (x *Account) Reset() { *x = Account{} - mi := &file_proxy_shadowsocks_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Account) String() string { @@ -105,7 +107,7 @@ func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -152,9 +154,11 @@ type ServerConfig struct { func (x *ServerConfig) Reset() { *x = ServerConfig{} - mi := &file_proxy_shadowsocks_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ServerConfig) String() string { @@ -165,7 +169,7 @@ func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -204,9 +208,11 @@ type ClientConfig struct { func (x *ClientConfig) Reset() { *x = ClientConfig{} - mi := &file_proxy_shadowsocks_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ClientConfig) String() string { @@ -217,7 +223,7 @@ func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -303,7 +309,7 @@ func file_proxy_shadowsocks_config_proto_rawDescGZIP() []byte { var file_proxy_shadowsocks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_proxy_shadowsocks_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_proxy_shadowsocks_config_proto_goTypes = []any{ +var file_proxy_shadowsocks_config_proto_goTypes = []interface{}{ (CipherType)(0), // 0: xray.proxy.shadowsocks.CipherType (*Account)(nil), // 1: xray.proxy.shadowsocks.Account (*ServerConfig)(nil), // 2: xray.proxy.shadowsocks.ServerConfig @@ -329,6 +335,44 @@ func file_proxy_shadowsocks_config_proto_init() { if File_proxy_shadowsocks_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_shadowsocks_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/shadowsocks/errors.generated.go b/proxy/shadowsocks/errors.generated.go new file mode 100644 index 00000000..dd33902b --- /dev/null +++ b/proxy/shadowsocks/errors.generated.go @@ -0,0 +1,9 @@ +package shadowsocks + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index c992f619..3176d118 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -4,7 +4,6 @@ import ( "crypto/hmac" "crypto/rand" "crypto/sha256" - goerrors "errors" "hash/crc32" "io" @@ -12,7 +11,6 @@ import ( "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/drain" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" ) @@ -59,7 +57,7 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe drainer, errDrain := drain.NewBehaviorSeedLimitedDrainer(int64(behaviorSeed), 16+38, 3266, 64) if errDrain != nil { - return nil, nil, errors.New("failed to initialize drainer").Base(errDrain) + return nil, nil, newError("failed to initialize drainer").Base(errDrain) } var r buf.Reader @@ -68,7 +66,7 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe if _, err := buffer.ReadFullFrom(reader, 50); err != nil { drainer.AcknowledgeReceive(int(buffer.Len())) - return nil, nil, drain.WithError(drainer, reader, errors.New("failed to read 50 bytes").Base(err)) + return nil, nil, drain.WithError(drainer, reader, newError("failed to read 50 bytes").Base(err)) } bs := buffer.Bytes() @@ -77,10 +75,10 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe switch err { case ErrNotFound: drainer.AcknowledgeReceive(int(buffer.Len())) - return nil, nil, drain.WithError(drainer, reader, errors.New("failed to match an user").Base(err)) + return nil, nil, drain.WithError(drainer, reader, newError("failed to match an user").Base(err)) case ErrIVNotUnique: drainer.AcknowledgeReceive(int(buffer.Len())) - return nil, nil, drain.WithError(drainer, reader, errors.New("failed iv check").Base(err)) + return nil, nil, drain.WithError(drainer, reader, newError("failed iv check").Base(err)) default: reader = &FullReader{reader, bs[ivLen:]} drainer.AcknowledgeReceive(int(ivLen)) @@ -98,7 +96,7 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe iv := append([]byte(nil), buffer.BytesTo(ivLen)...) r, err = account.Cipher.NewDecryptionReader(account.Key, iv, reader) if err != nil { - return nil, nil, drain.WithError(drainer, reader, errors.New("failed to initialize decoding stream").Base(err).AtError()) + return nil, nil, drain.WithError(drainer, reader, newError("failed to initialize decoding stream").Base(err).AtError()) } } } @@ -116,7 +114,7 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe addr, port, err := addrParser.ReadAddressPort(buffer, br) if err != nil { drainer.AcknowledgeReceive(int(buffer.Len())) - return nil, nil, drain.WithError(drainer, reader, errors.New("failed to read address").Base(err)) + return nil, nil, drain.WithError(drainer, reader, newError("failed to read address").Base(err)) } request.Address = addr @@ -124,7 +122,7 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe if request.Address == nil { drainer.AcknowledgeReceive(int(buffer.Len())) - return nil, nil, drain.WithError(drainer, reader, errors.New("invalid remote address.")) + return nil, nil, drain.WithError(drainer, reader, newError("invalid remote address.")) } return request, br, nil @@ -140,26 +138,26 @@ func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Wri iv = make([]byte, account.Cipher.IVSize()) common.Must2(rand.Read(iv)) if ivError := account.CheckIV(iv); ivError != nil { - return nil, errors.New("failed to mark outgoing iv").Base(ivError) + return nil, newError("failed to mark outgoing iv").Base(ivError) } if err := buf.WriteAllBytes(writer, iv, nil); err != nil { - return nil, errors.New("failed to write IV") + return nil, newError("failed to write IV") } } w, err := account.Cipher.NewEncryptionWriter(account.Key, iv, writer) if err != nil { - return nil, errors.New("failed to create encoding stream").Base(err).AtError() + return nil, newError("failed to create encoding stream").Base(err).AtError() } header := buf.New() if err := addrParser.WriteAddressPort(header, request.Address, request.Port); err != nil { - return nil, errors.New("failed to write address").Base(err) + return nil, newError("failed to write address").Base(err) } if err := w.WriteMultiBuffer(buf.MultiBuffer{header}); err != nil { - return nil, errors.New("failed to write header").Base(err) + return nil, newError("failed to write header").Base(err) } return w, nil @@ -175,21 +173,21 @@ func ReadTCPResponse(user *protocol.MemoryUser, reader io.Reader) (buf.Reader, e drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(behaviorSeed), 16+38, 3266, 64) if err != nil { - return nil, errors.New("failed to initialize drainer").Base(err) + return nil, newError("failed to initialize drainer").Base(err) } var iv []byte if account.Cipher.IVSize() > 0 { iv = make([]byte, account.Cipher.IVSize()) if n, err := io.ReadFull(reader, iv); err != nil { - return nil, errors.New("failed to read IV").Base(err) + return nil, newError("failed to read IV").Base(err) } else { // nolint: golint drainer.AcknowledgeReceive(n) } } if ivError := account.CheckIV(iv); ivError != nil { - return nil, drain.WithError(drainer, reader, errors.New("failed iv check").Base(ivError)) + return nil, drain.WithError(drainer, reader, newError("failed iv check").Base(ivError)) } return account.Cipher.NewDecryptionReader(account.Key, iv, reader) @@ -204,10 +202,10 @@ func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (buf.Wr iv = make([]byte, account.Cipher.IVSize()) common.Must2(rand.Read(iv)) if ivError := account.CheckIV(iv); ivError != nil { - return nil, errors.New("failed to mark outgoing iv").Base(ivError) + return nil, newError("failed to mark outgoing iv").Base(ivError) } if err := buf.WriteAllBytes(writer, iv, nil); err != nil { - return nil, errors.New("failed to write IV.").Base(err) + return nil, newError("failed to write IV.").Base(err) } } @@ -225,67 +223,62 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff } if err := addrParser.WriteAddressPort(buffer, request.Address, request.Port); err != nil { - return nil, errors.New("failed to write address").Base(err) + return nil, newError("failed to write address").Base(err) } buffer.Write(payload) if err := account.Cipher.EncodePacket(account.Key, buffer); err != nil { - return nil, errors.New("failed to encrypt UDP payload").Base(err) + return nil, newError("failed to encrypt UDP payload").Base(err) } return buffer, nil } func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) { - rawPayload := payload.Bytes() - user, _, d, _, err := validator.Get(rawPayload, protocol.RequestCommandUDP) - - if goerrors.Is(err, ErrIVNotUnique) { - return nil, nil, errors.New("failed iv check").Base(err) + bs := payload.Bytes() + if len(bs) <= 32 { + return nil, nil, newError("len(bs) <= 32") } - if goerrors.Is(err, ErrNotFound) { - return nil, nil, errors.New("failed to match an user").Base(err) - } - - if err != nil { - return nil, nil, errors.New("unexpected error").Base(err) - } - - account, ok := user.Account.(*MemoryAccount) - if !ok { - return nil, nil, errors.New("expected MemoryAccount returned from validator") - } - - if account.Cipher.IsAEAD() { - payload.Clear() - payload.Write(d) - } else { - if account.Cipher.IVSize() > 0 { - iv := make([]byte, account.Cipher.IVSize()) - copy(iv, payload.BytesTo(account.Cipher.IVSize())) + user, _, d, _, err := validator.Get(bs, protocol.RequestCommandUDP) + switch err { + case ErrIVNotUnique: + return nil, nil, newError("failed iv check").Base(err) + case ErrNotFound: + return nil, nil, newError("failed to match an user").Base(err) + default: + account := user.Account.(*MemoryAccount) + if account.Cipher.IsAEAD() { + payload.Clear() + payload.Write(d) + } else { + if account.Cipher.IVSize() > 0 { + iv := make([]byte, account.Cipher.IVSize()) + copy(iv, payload.BytesTo(account.Cipher.IVSize())) + } + if err = account.Cipher.DecodePacket(account.Key, payload); err != nil { + return nil, nil, newError("failed to decrypt UDP payload").Base(err) + } } - if err = account.Cipher.DecodePacket(account.Key, payload); err != nil { - return nil, nil, errors.New("failed to decrypt UDP payload").Base(err) - } - } - - payload.SetByte(0, payload.Byte(0)&0x0F) - - addr, port, err := addrParser.ReadAddressPort(nil, payload) - if err != nil { - return nil, nil, errors.New("failed to parse address").Base(err) } request := &protocol.RequestHeader{ Version: Version, User: user, Command: protocol.RequestCommandUDP, - Address: addr, - Port: port, } + payload.SetByte(0, payload.Byte(0)&0x0F) + + addr, port, err := addrParser.ReadAddressPort(nil, payload) + if err != nil { + return nil, nil, newError("failed to parse address").Base(err) + } + + request.Address = addr + request.Port = port + return request, payload, nil } diff --git a/proxy/shadowsocks/protocol_test.go b/proxy/shadowsocks/protocol_test.go index 4083905d..e1b6495e 100644 --- a/proxy/shadowsocks/protocol_test.go +++ b/proxy/shadowsocks/protocol_test.go @@ -23,80 +23,37 @@ func equalRequestHeader(x, y *protocol.RequestHeader) bool { })) } -func TestUDPEncodingDecoding(t *testing.T) { - testRequests := []protocol.RequestHeader{ - { - Version: Version, - Command: protocol.RequestCommandUDP, - Address: net.LocalHostIP, - Port: 1234, - User: &protocol.MemoryUser{ - Email: "love@example.com", - Account: toAccount(&Account{ - Password: "password", - CipherType: CipherType_AES_128_GCM, - }), - }, - }, - { - Version: Version, - Command: protocol.RequestCommandUDP, - Address: net.LocalHostIP, - Port: 1234, - User: &protocol.MemoryUser{ - Email: "love@example.com", - Account: toAccount(&Account{ - Password: "123", - CipherType: CipherType_NONE, - }), - }, +func TestUDPEncoding(t *testing.T) { + request := &protocol.RequestHeader{ + Version: Version, + Command: protocol.RequestCommandUDP, + Address: net.LocalHostIP, + Port: 1234, + User: &protocol.MemoryUser{ + Email: "love@example.com", + Account: toAccount(&Account{ + Password: "password", + CipherType: CipherType_AES_128_GCM, + }), }, } - for _, request := range testRequests { - data := buf.New() - common.Must2(data.WriteString("test string")) - encodedData, err := EncodeUDPPacket(&request, data.Bytes()) - common.Must(err) + data := buf.New() + common.Must2(data.WriteString("test string")) + encodedData, err := EncodeUDPPacket(request, data.Bytes()) + common.Must(err) - validator := new(Validator) - validator.Add(request.User) - decodedRequest, decodedData, err := DecodeUDPPacket(validator, encodedData) - common.Must(err) + validator := new(Validator) + validator.Add(request.User) + decodedRequest, decodedData, err := DecodeUDPPacket(validator, encodedData) + common.Must(err) - if r := cmp.Diff(decodedData.Bytes(), data.Bytes()); r != "" { - t.Error("data: ", r) - } - - if equalRequestHeader(decodedRequest, &request) == false { - t.Error("different request") - } - } -} - -func TestUDPDecodingWithPayloadTooShort(t *testing.T) { - testAccounts := []protocol.Account{ - toAccount(&Account{ - Password: "password", - CipherType: CipherType_AES_128_GCM, - }), - toAccount(&Account{ - Password: "password", - CipherType: CipherType_NONE, - }), + if r := cmp.Diff(decodedData.Bytes(), data.Bytes()); r != "" { + t.Error("data: ", r) } - for _, account := range testAccounts { - data := buf.New() - data.WriteString("short payload") - validator := new(Validator) - validator.Add(&protocol.MemoryUser{ - Account: account, - }) - _, _, err := DecodeUDPPacket(validator, data) - if err == nil { - t.Fatal("expected error") - } + if equalRequestHeader(decodedRequest, request) == false { + t.Error("different request") } } diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 356868e4..140c6704 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -6,7 +6,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" @@ -34,11 +33,11 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { for _, user := range config.Users { u, err := user.ToMemoryUser() if err != nil { - return nil, errors.New("failed to get shadowsocks user").Base(err).AtError() + return nil, newError("failed to get shadowsocks user").Base(err).AtError() } if err := validator.Add(u); err != nil { - return nil, errors.New("failed to add user").Base(err).AtError() + return nil, newError("failed to add user").Base(err).AtError() } } @@ -63,21 +62,6 @@ func (s *Server) RemoveUser(ctx context.Context, e string) error { return s.validator.Del(e) } -// GetUser implements proxy.UserManager.GetUser(). -func (s *Server) GetUser(ctx context.Context, email string) *protocol.MemoryUser { - return s.validator.GetByEmail(email) -} - -// GetUsers implements proxy.UserManager.GetUsers(). -func (s *Server) GetUsers(ctx context.Context) []*protocol.MemoryUser { - return s.validator.GetAll() -} - -// GetUsersCount implements proxy.UserManager.GetUsersCount(). -func (s *Server) GetUsersCount(context.Context) int64 { - return s.validator.GetCount() -} - func (s *Server) Network() []net.Network { list := s.config.Network if len(list) == 0 { @@ -87,17 +71,13 @@ func (s *Server) Network() []net.Network { } func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { - inbound := session.InboundFromContext(ctx) - inbound.Name = "shadowsocks" - inbound.CanSpliceCopy = 3 - switch network { case net.Network_TCP: return s.handleConnection(ctx, conn, dispatcher) case net.Network_UDP: return s.handleUDPPayload(ctx, conn, dispatcher) default: - return errors.New("unknown network: ", network) + return newError("unknown network: ", network) } } @@ -121,7 +101,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis data, err := EncodeUDPPacket(request, payload.Bytes()) payload.Release() if err != nil { - errors.LogWarningInner(ctx, err, "failed to encode UDP packet") + newError("failed to encode UDP packet").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) return } defer data.Release() @@ -130,7 +110,12 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis }) inbound := session.InboundFromContext(ctx) + if inbound == nil { + panic("no inbound metadata") + } + var dest *net.Destination + reader := buf.NewPacketReader(conn) for { mpayload, err := reader.ReadMultiBuffer() @@ -156,7 +141,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis if err != nil { if inbound.Source.IsValid() { - errors.LogInfoInner(ctx, err, "dropping invalid UDP packet from: ", inbound.Source) + newError("dropping invalid UDP packet from: ", inbound.Source).Base(err).WriteToLog(session.ExportIDToError(ctx)) log.Record(&log.AccessMessage{ From: inbound.Source, To: "", @@ -180,7 +165,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis Email: request.User.Email, }) } - errors.LogInfo(ctx, "tunnelling request to ", destination) + newError("tunnelling request to ", destination).WriteToLog(session.ExportIDToError(currentPacketCtx)) data.UDP = &destination @@ -199,7 +184,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis func (s *Server) handleConnection(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error { sessionPolicy := s.policyManager.ForLevel(0) if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { - return errors.New("unable to set read deadline").Base(err).AtWarning() + return newError("unable to set read deadline").Base(err).AtWarning() } bufferedReader := buf.BufferedReader{Reader: buf.NewReader(conn)} @@ -211,7 +196,7 @@ func (s *Server) handleConnection(ctx context.Context, conn stat.Connection, dis Status: log.AccessRejected, Reason: err, }) - return errors.New("failed to create request from: ", conn.RemoteAddr()).Base(err) + return newError("failed to create request from: ", conn.RemoteAddr()).Base(err) } conn.SetReadDeadline(time.Time{}) @@ -229,7 +214,7 @@ func (s *Server) handleConnection(ctx context.Context, conn stat.Connection, dis Reason: "", Email: request.User.Email, }) - errors.LogInfo(ctx, "tunnelling request to ", dest) + newError("tunnelling request to ", dest).WriteToLog(session.ExportIDToError(ctx)) sessionPolicy = s.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) @@ -247,7 +232,7 @@ func (s *Server) handleConnection(ctx context.Context, conn stat.Connection, dis bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) responseWriter, err := WriteTCPResponse(request, bufferedWriter) if err != nil { - return errors.New("failed to write response").Base(err) + return newError("failed to write response").Base(err) } { @@ -265,7 +250,7 @@ func (s *Server) handleConnection(ctx context.Context, conn stat.Connection, dis } if err := buf.Copy(link.Reader, responseWriter, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all TCP response").Base(err) + return newError("failed to transport all TCP response").Base(err) } return nil @@ -275,7 +260,7 @@ func (s *Server) handleConnection(ctx context.Context, conn stat.Connection, dis defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all TCP request").Base(err) + return newError("failed to transport all TCP request").Base(err) } return nil @@ -285,7 +270,7 @@ func (s *Server) handleConnection(ctx context.Context, conn stat.Connection, dis if err := task.Run(ctx, requestDoneAndCloseWriter, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil diff --git a/proxy/shadowsocks/shadowsocks.go b/proxy/shadowsocks/shadowsocks.go index 7bb16943..6d950db2 100644 --- a/proxy/shadowsocks/shadowsocks.go +++ b/proxy/shadowsocks/shadowsocks.go @@ -4,3 +4,5 @@ // // R.I.P Shadowsocks package shadowsocks + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/proxy/shadowsocks/validator.go b/proxy/shadowsocks/validator.go index 84e59dc1..2aa62e06 100644 --- a/proxy/shadowsocks/validator.go +++ b/proxy/shadowsocks/validator.go @@ -9,7 +9,6 @@ import ( "sync" "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" ) @@ -22,7 +21,7 @@ type Validator struct { behaviorFused bool } -var ErrNotFound = errors.New("Not Found") +var ErrNotFound = newError("Not Found") // Add a Shadowsocks user. func (v *Validator) Add(u *protocol.MemoryUser) error { @@ -31,7 +30,7 @@ func (v *Validator) Add(u *protocol.MemoryUser) error { account := u.Account.(*MemoryAccount) if !account.Cipher.IsAEAD() && len(v.users) > 0 { - return errors.New("The cipher is not support Single-port Multi-user") + return newError("The cipher is not support Single-port Multi-user") } v.users = append(v.users, u) @@ -47,7 +46,7 @@ func (v *Validator) Add(u *protocol.MemoryUser) error { // Del a Shadowsocks user with a non-empty Email. func (v *Validator) Del(email string) error { if email == "" { - return errors.New("Email must not be empty.") + return newError("Email must not be empty.") } v.Lock() @@ -63,7 +62,7 @@ func (v *Validator) Del(email string) error { } if idx == -1 { - return errors.New("User ", email, " not found.") + return newError("User ", email, " not found.") } ulen := len(v.users) @@ -74,40 +73,6 @@ func (v *Validator) Del(email string) error { return nil } -// GetByEmail Get a Shadowsocks user with a non-empty Email. -func (v *Validator) GetByEmail(email string) *protocol.MemoryUser { - if email == "" { - return nil - } - - v.Lock() - defer v.Unlock() - - email = strings.ToLower(email) - for _, u := range v.users { - if strings.EqualFold(u.Email, email) { - return u - } - } - return nil -} - -// GetAll get all users -func (v *Validator) GetAll() []*protocol.MemoryUser { - v.Lock() - defer v.Unlock() - dst := make([]*protocol.MemoryUser, len(v.users)) - copy(dst, v.users) - return dst -} - -// GetCount get users count -func (v *Validator) GetCount() int64 { - v.Lock() - defer v.Unlock() - return int64(len(v.users)) -} - // Get a Shadowsocks user. func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol.MemoryUser, aead cipher.AEAD, ret []byte, ivLen int32, err error) { v.RLock() @@ -115,11 +80,6 @@ func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol for _, user := range v.users { if account := user.Account.(*MemoryAccount); account.Cipher.IsAEAD() { - // AEAD payload decoding requires the payload to be over 32 bytes - if len(bs) < 32 { - continue - } - aeadCipher := account.Cipher.(*AEADCipher) ivLen = aeadCipher.IVSize() iv := bs[:ivLen] diff --git a/proxy/shadowsocks_2022/config.go b/proxy/shadowsocks_2022/config.go index 9ddd2cf8..8a66406c 100644 --- a/proxy/shadowsocks_2022/config.go +++ b/proxy/shadowsocks_2022/config.go @@ -1,20 +1,22 @@ package shadowsocks_2022 import ( - "google.golang.org/protobuf/proto" - "github.com/xtls/xray-core/common/protocol" ) // MemoryAccount is an account type converted from Account. type MemoryAccount struct { - Key string + Key string + Email string + Level int32 } // AsAccount implements protocol.AsAccount. -func (u *Account) AsAccount() (protocol.Account, error) { +func (u *User) AsAccount() (protocol.Account, error) { return &MemoryAccount{ - Key: u.GetKey(), + Key: u.GetKey(), + Email: u.GetEmail(), + Level: u.GetLevel(), }, nil } @@ -25,9 +27,3 @@ func (a *MemoryAccount) Equals(another protocol.Account) bool { } return false } - -func (a *MemoryAccount) ToProto() proto.Message { - return &Account{ - Key: a.Key, - } -} diff --git a/proxy/shadowsocks_2022/config.pb.go b/proxy/shadowsocks_2022/config.pb.go index f2f78ad6..50626f7a 100644 --- a/proxy/shadowsocks_2022/config.pb.go +++ b/proxy/shadowsocks_2022/config.pb.go @@ -1,14 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/shadowsocks_2022/config.proto package shadowsocks_2022 import ( net "github.com/xtls/xray-core/common/net" - protocol "github.com/xtls/xray-core/common/protocol" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -36,9 +35,11 @@ type ServerConfig struct { func (x *ServerConfig) Reset() { *x = ServerConfig{} - mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ServerConfig) String() string { @@ -49,7 +50,7 @@ func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -104,17 +105,19 @@ type MultiUserServerConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` - Users []*protocol.User `protobuf:"bytes,3,rep,name=users,proto3" json:"users,omitempty"` - Network []net.Network `protobuf:"varint,4,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"` + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Users []*User `protobuf:"bytes,3,rep,name=users,proto3" json:"users,omitempty"` + Network []net.Network `protobuf:"varint,4,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"` } func (x *MultiUserServerConfig) Reset() { *x = MultiUserServerConfig{} - mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *MultiUserServerConfig) String() string { @@ -125,7 +128,7 @@ func (*MultiUserServerConfig) ProtoMessage() {} func (x *MultiUserServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -154,7 +157,7 @@ func (x *MultiUserServerConfig) GetKey() string { return "" } -func (x *MultiUserServerConfig) GetUsers() []*protocol.User { +func (x *MultiUserServerConfig) GetUsers() []*User { if x != nil { return x.Users } @@ -182,9 +185,11 @@ type RelayDestination struct { func (x *RelayDestination) Reset() { *x = RelayDestination{} - mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RelayDestination) String() string { @@ -195,7 +200,7 @@ func (*RelayDestination) ProtoMessage() {} func (x *RelayDestination) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -258,9 +263,11 @@ type RelayServerConfig struct { func (x *RelayServerConfig) Reset() { *x = RelayServerConfig{} - mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RelayServerConfig) String() string { @@ -271,7 +278,7 @@ func (*RelayServerConfig) ProtoMessage() {} func (x *RelayServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -314,30 +321,34 @@ func (x *RelayServerConfig) GetNetwork() []net.Network { return nil } -type Account struct { +type User struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + Level int32 `protobuf:"varint,3,opt,name=level,proto3" json:"level,omitempty"` } -func (x *Account) Reset() { - *x = Account{} - mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (x *Account) String() string { +func (x *User) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Account) ProtoMessage() {} +func (*User) ProtoMessage() {} -func (x *Account) ProtoReflect() protoreflect.Message { +func (x *User) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -347,36 +358,51 @@ func (x *Account) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Account.ProtoReflect.Descriptor instead. -func (*Account) Descriptor() ([]byte, []int) { +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{4} } -func (x *Account) GetKey() string { +func (x *User) GetKey() string { if x != nil { return x.Key } return "" } +func (x *User) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *User) GetLevel() int32 { + if x != nil { + return x.Level + } + return 0 +} + type ClientConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` - Key string `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"` - UdpOverTcp bool `protobuf:"varint,5,opt,name=udp_over_tcp,json=udpOverTcp,proto3" json:"udp_over_tcp,omitempty"` - UdpOverTcpVersion uint32 `protobuf:"varint,6,opt,name=udp_over_tcp_version,json=udpOverTcpVersion,proto3" json:"udp_over_tcp_version,omitempty"` + Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` + Key string `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"` + UdpOverTcp bool `protobuf:"varint,5,opt,name=udp_over_tcp,json=udpOverTcp,proto3" json:"udp_over_tcp,omitempty"` } func (x *ClientConfig) Reset() { *x = ClientConfig{} - mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ClientConfig) String() string { @@ -387,7 +413,7 @@ func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -437,13 +463,6 @@ func (x *ClientConfig) GetUdpOverTcp() bool { return false } -func (x *ClientConfig) GetUdpOverTcpVersion() uint32 { - if x != nil { - return x.UdpOverTcpVersion - } - return 0 -} - var File_proxy_shadowsocks_2022_config_proto protoreflect.FileDescriptor var file_proxy_shadowsocks_2022_config_proto_rawDesc = []byte{ @@ -454,74 +473,73 @@ var file_proxy_shadowsocks_2022_config_proto_rawDesc = []byte{ 0x32, 0x32, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, - 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0xa7, 0x01, - 0x0a, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x30, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, - 0x65, 0x72, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x9b, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x61, - 0x79, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 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, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, - 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, - 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, - 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0xc4, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x32, 0x0a, + 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, + 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x22, 0xae, 0x01, 0x0a, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x51, 0x0a, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, - 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, 0x32, 0x32, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x44, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x1b, 0x0a, 0x07, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 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, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x20, 0x0a, 0x0c, 0x75, 0x64, 0x70, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x63, 0x70, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x64, 0x70, 0x4f, 0x76, 0x65, 0x72, 0x54, 0x63, - 0x70, 0x12, 0x2f, 0x0a, 0x14, 0x75, 0x64, 0x70, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x63, - 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x11, 0x75, 0x64, 0x70, 0x4f, 0x76, 0x65, 0x72, 0x54, 0x63, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x42, 0x72, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, - 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, - 0x5f, 0x32, 0x30, 0x32, 0x32, 0x50, 0x01, 0x5a, 0x30, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, - 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, 0x32, 0x32, 0xaa, 0x02, 0x1a, 0x58, 0x72, 0x61, 0x79, - 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, - 0x6b, 0x73, 0x32, 0x30, 0x32, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, + 0x32, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x32, + 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, + 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x22, 0x9b, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x44, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 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, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x70, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x22, 0xc4, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x51, 0x0a, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x5f, + 0x32, 0x30, 0x32, 0x32, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x44, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0xa5, 0x01, + 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 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, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x64, 0x70, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, + 0x74, 0x63, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x64, 0x70, 0x4f, 0x76, + 0x65, 0x72, 0x54, 0x63, 0x70, 0x42, 0x72, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, + 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, 0x32, 0x32, 0x50, 0x01, 0x5a, 0x30, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, + 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, 0x32, 0x32, 0xaa, 0x02, 0x1a, 0x58, + 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, + 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x32, 0x30, 0x32, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -537,25 +555,24 @@ func file_proxy_shadowsocks_2022_config_proto_rawDescGZIP() []byte { } var file_proxy_shadowsocks_2022_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_proxy_shadowsocks_2022_config_proto_goTypes = []any{ +var file_proxy_shadowsocks_2022_config_proto_goTypes = []interface{}{ (*ServerConfig)(nil), // 0: xray.proxy.shadowsocks_2022.ServerConfig (*MultiUserServerConfig)(nil), // 1: xray.proxy.shadowsocks_2022.MultiUserServerConfig (*RelayDestination)(nil), // 2: xray.proxy.shadowsocks_2022.RelayDestination (*RelayServerConfig)(nil), // 3: xray.proxy.shadowsocks_2022.RelayServerConfig - (*Account)(nil), // 4: xray.proxy.shadowsocks_2022.Account + (*User)(nil), // 4: xray.proxy.shadowsocks_2022.User (*ClientConfig)(nil), // 5: xray.proxy.shadowsocks_2022.ClientConfig (net.Network)(0), // 6: xray.common.net.Network - (*protocol.User)(nil), // 7: xray.common.protocol.User - (*net.IPOrDomain)(nil), // 8: xray.common.net.IPOrDomain + (*net.IPOrDomain)(nil), // 7: xray.common.net.IPOrDomain } var file_proxy_shadowsocks_2022_config_proto_depIdxs = []int32{ 6, // 0: xray.proxy.shadowsocks_2022.ServerConfig.network:type_name -> xray.common.net.Network - 7, // 1: xray.proxy.shadowsocks_2022.MultiUserServerConfig.users:type_name -> xray.common.protocol.User + 4, // 1: xray.proxy.shadowsocks_2022.MultiUserServerConfig.users:type_name -> xray.proxy.shadowsocks_2022.User 6, // 2: xray.proxy.shadowsocks_2022.MultiUserServerConfig.network:type_name -> xray.common.net.Network - 8, // 3: xray.proxy.shadowsocks_2022.RelayDestination.address:type_name -> xray.common.net.IPOrDomain + 7, // 3: xray.proxy.shadowsocks_2022.RelayDestination.address:type_name -> xray.common.net.IPOrDomain 2, // 4: xray.proxy.shadowsocks_2022.RelayServerConfig.destinations:type_name -> xray.proxy.shadowsocks_2022.RelayDestination 6, // 5: xray.proxy.shadowsocks_2022.RelayServerConfig.network:type_name -> xray.common.net.Network - 8, // 6: xray.proxy.shadowsocks_2022.ClientConfig.address:type_name -> xray.common.net.IPOrDomain + 7, // 6: xray.proxy.shadowsocks_2022.ClientConfig.address:type_name -> xray.common.net.IPOrDomain 7, // [7:7] is the sub-list for method output_type 7, // [7:7] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name @@ -568,6 +585,80 @@ func file_proxy_shadowsocks_2022_config_proto_init() { if File_proxy_shadowsocks_2022_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_shadowsocks_2022_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_2022_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MultiUserServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_2022_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RelayDestination); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_2022_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RelayServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_2022_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_2022_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/shadowsocks_2022/config.proto b/proxy/shadowsocks_2022/config.proto index 14006648..2c4690e4 100644 --- a/proxy/shadowsocks_2022/config.proto +++ b/proxy/shadowsocks_2022/config.proto @@ -8,7 +8,6 @@ option java_multiple_files = true; import "common/net/network.proto"; import "common/net/address.proto"; -import "common/protocol/user.proto"; message ServerConfig { string method = 1; @@ -21,7 +20,7 @@ message ServerConfig { message MultiUserServerConfig { string method = 1; string key = 2; - repeated xray.common.protocol.User users = 3; + repeated User users = 3; repeated xray.common.net.Network network = 4; } @@ -40,8 +39,10 @@ message RelayServerConfig { repeated xray.common.net.Network network = 4; } -message Account { +message User { string key = 1; + string email = 2; + int32 level = 3; } message ClientConfig { @@ -50,5 +51,4 @@ message ClientConfig { string method = 3; string key = 4; bool udp_over_tcp = 5; - uint32 udp_over_tcp_version = 6; } diff --git a/proxy/shadowsocks_2022/errors.generated.go b/proxy/shadowsocks_2022/errors.generated.go new file mode 100644 index 00000000..90db3d5e --- /dev/null +++ b/proxy/shadowsocks_2022/errors.generated.go @@ -0,0 +1,9 @@ +package shadowsocks_2022 + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/shadowsocks_2022/inbound.go b/proxy/shadowsocks_2022/inbound.go index a889fa0f..55bdda9f 100644 --- a/proxy/shadowsocks_2022/inbound.go +++ b/proxy/shadowsocks_2022/inbound.go @@ -3,7 +3,7 @@ package shadowsocks_2022 import ( "context" - shadowsocks "github.com/sagernet/sing-shadowsocks" + "github.com/sagernet/sing-shadowsocks" "github.com/sagernet/sing-shadowsocks/shadowaead_2022" C "github.com/sagernet/sing/common" B "github.com/sagernet/sing/common/buf" @@ -13,12 +13,10 @@ import ( N "github.com/sagernet/sing/common/network" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/singbridge" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet/stat" ) @@ -50,11 +48,11 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Inbound, error) { level: int(config.Level), } if !C.Contains(shadowaead_2022.List, config.Method) { - return nil, errors.New("unsupported method ", config.Method) + return nil, newError("unsupported method ", config.Method) } - service, err := shadowaead_2022.NewServiceWithPassword(config.Method, config.Key, 500, inbound, nil) + service, err := shadowaead_2022.NewServiceWithPassword(config.Method, config.Key, 500, inbound) if err != nil { - return nil, errors.New("create service").Base(err) + return nil, newError("create service").Base(err) } inbound.service = service return inbound, nil @@ -66,8 +64,6 @@ func (i *Inbound) Network() []net.Network { func (i *Inbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { inbound := session.InboundFromContext(ctx) - inbound.Name = "shadowsocks-2022" - inbound.CanSpliceCopy = 3 var metadata M.Metadata if inbound.Source.IsValid() { @@ -77,7 +73,7 @@ func (i *Inbound) Process(ctx context.Context, network net.Network, connection s ctx = session.ContextWithDispatcher(ctx, dispatcher) if network == net.Network_TCP { - return singbridge.ReturnError(i.service.NewConnection(ctx, connection, metadata)) + return returnError(i.service.NewConnection(ctx, connection, metadata)) } else { reader := buf.NewReader(connection) pc := &natPacketConn{connection} @@ -85,17 +81,17 @@ func (i *Inbound) Process(ctx context.Context, network net.Network, connection s mb, err := reader.ReadMultiBuffer() if err != nil { buf.ReleaseMulti(mb) - return singbridge.ReturnError(err) + return returnError(err) } for _, buffer := range mb { packet := B.As(buffer.Bytes()).ToOwned() - buffer.Release() err = i.service.NewPacket(ctx, pc, packet, metadata) if err != nil { packet.Release() buf.ReleaseMulti(mb) return err } + buffer.Release() } } } @@ -113,13 +109,18 @@ func (i *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata M.M Status: log.AccessAccepted, Email: i.email, }) - errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination) + newError("tunnelling request to tcp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) dispatcher := session.DispatcherFromContext(ctx) - link, err := dispatcher.Dispatch(ctx, singbridge.ToDestination(metadata.Destination, net.Network_TCP)) + link, err := dispatcher.Dispatch(ctx, toDestination(metadata.Destination, net.Network_TCP)) if err != nil { return err } - return singbridge.CopyConn(ctx, nil, link, conn) + outConn := &pipeConnWrapper{ + &buf.BufferedReader{Reader: link.Reader}, + link.Writer, + conn, + } + return bufio.CopyConn(ctx, conn, outConn) } func (i *Inbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { @@ -134,14 +135,14 @@ func (i *Inbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, me Status: log.AccessAccepted, Email: i.email, }) - errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination) + newError("tunnelling request to udp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) dispatcher := session.DispatcherFromContext(ctx) - destination := singbridge.ToDestination(metadata.Destination, net.Network_UDP) + destination := toDestination(metadata.Destination, net.Network_UDP) link, err := dispatcher.Dispatch(ctx, destination) if err != nil { return err } - outConn := &singbridge.PacketConnWrapper{ + outConn := &packetConnWrapper{ Reader: link.Reader, Writer: link.Writer, Dest: destination, @@ -153,7 +154,7 @@ func (i *Inbound) NewError(ctx context.Context, err error) { if E.IsClosed(err) { return } - errors.LogWarning(ctx, err.Error()) + newError(err).AtWarning().WriteToLog() } type natPacketConn struct { diff --git a/proxy/shadowsocks_2022/inbound_multi.go b/proxy/shadowsocks_2022/inbound_multi.go index 4bfa086a..662a171c 100644 --- a/proxy/shadowsocks_2022/inbound_multi.go +++ b/proxy/shadowsocks_2022/inbound_multi.go @@ -17,12 +17,10 @@ import ( N "github.com/sagernet/sing/common/network" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/singbridge" "github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet/stat" @@ -37,7 +35,7 @@ func init() { type MultiUserInbound struct { sync.Mutex networks []net.Network - users []*protocol.MemoryUser + users []*User service *shadowaead_2022.MultiService[int] } @@ -49,40 +47,34 @@ func NewMultiServer(ctx context.Context, config *MultiUserServerConfig) (*MultiU net.Network_UDP, } } - memUsers := []*protocol.MemoryUser{} + inbound := &MultiUserInbound{ + networks: networks, + users: config.Users, + } + if config.Key == "" { + return nil, newError("missing key") + } + psk, err := base64.StdEncoding.DecodeString(config.Key) + if err != nil { + return nil, newError("parse config").Base(err) + } + service, err := shadowaead_2022.NewMultiService[int](config.Method, psk, 500, inbound) + if err != nil { + return nil, newError("create service").Base(err) + } + for i, user := range config.Users { if user.Email == "" { u := uuid.New() user.Email = "unnamed-user-" + strconv.Itoa(i) + "-" + u.String() } - u, err := user.ToMemoryUser() - if err != nil { - return nil, errors.New("failed to get shadowsocks user").Base(err).AtError() - } - memUsers = append(memUsers, u) - } - - inbound := &MultiUserInbound{ - networks: networks, - users: memUsers, - } - if config.Key == "" { - return nil, errors.New("missing key") - } - psk, err := base64.StdEncoding.DecodeString(config.Key) - if err != nil { - return nil, errors.New("parse config").Base(err) - } - service, err := shadowaead_2022.NewMultiService[int](config.Method, psk, 500, inbound, nil) - if err != nil { - return nil, errors.New("create service").Base(err) } err = service.UpdateUsersWithPasswords( - C.MapIndexed(memUsers, func(index int, it *protocol.MemoryUser) int { return index }), - C.Map(memUsers, func(it *protocol.MemoryUser) string { return it.Account.(*MemoryAccount).Key }), + C.MapIndexed(config.Users, func(index int, it *User) int { return index }), + C.Map(config.Users, func(it *User) string { return it.Key }), ) if err != nil { - return nil, errors.New("create service").Base(err) + return nil, newError("create service").Base(err) } inbound.service = service @@ -94,20 +86,25 @@ func (i *MultiUserInbound) AddUser(ctx context.Context, u *protocol.MemoryUser) i.Lock() defer i.Unlock() - if u.Email != "" { + account := u.Account.(*MemoryAccount) + if account.Email != "" { for idx := range i.users { - if i.users[idx].Email == u.Email { - return errors.New("User ", u.Email, " already exists.") + if i.users[idx].Email == account.Email { + return newError("User ", account.Email, " already exists.") } } } - i.users = append(i.users, u) + i.users = append(i.users, &User{ + Key: account.Key, + Email: account.Email, + Level: account.Level, + }) // sync to multi service // Considering implements shadowsocks2022 in xray-core may have better performance. i.service.UpdateUsersWithPasswords( - C.MapIndexed(i.users, func(index int, it *protocol.MemoryUser) int { return index }), - C.Map(i.users, func(it *protocol.MemoryUser) string { return it.Account.(*MemoryAccount).Key }), + C.MapIndexed(i.users, func(index int, it *User) int { return index }), + C.Map(i.users, func(it *User) string { return it.Key }), ) return nil @@ -116,7 +113,7 @@ func (i *MultiUserInbound) AddUser(ctx context.Context, u *protocol.MemoryUser) // RemoveUser implements proxy.UserManager.RemoveUser(). func (i *MultiUserInbound) RemoveUser(ctx context.Context, email string) error { if email == "" { - return errors.New("Email must not be empty.") + return newError("Email must not be empty.") } i.Lock() @@ -131,7 +128,7 @@ func (i *MultiUserInbound) RemoveUser(ctx context.Context, email string) error { } if idx == -1 { - return errors.New("User ", email, " not found.") + return newError("User ", email, " not found.") } ulen := len(i.users) @@ -143,54 +140,19 @@ func (i *MultiUserInbound) RemoveUser(ctx context.Context, email string) error { // sync to multi service // Considering implements shadowsocks2022 in xray-core may have better performance. i.service.UpdateUsersWithPasswords( - C.MapIndexed(i.users, func(index int, it *protocol.MemoryUser) int { return index }), - C.Map(i.users, func(it *protocol.MemoryUser) string { return it.Account.(*MemoryAccount).Key }), + C.MapIndexed(i.users, func(index int, it *User) int { return index }), + C.Map(i.users, func(it *User) string { return it.Key }), ) return nil } -// GetUser implements proxy.UserManager.GetUser(). -func (i *MultiUserInbound) GetUser(ctx context.Context, email string) *protocol.MemoryUser { - if email == "" { - return nil - } - - i.Lock() - defer i.Unlock() - - for _, u := range i.users { - if strings.EqualFold(u.Email, email) { - return u - } - } - return nil -} - -// GetUsers implements proxy.UserManager.GetUsers(). -func (i *MultiUserInbound) GetUsers(ctx context.Context) []*protocol.MemoryUser { - i.Lock() - defer i.Unlock() - dst := make([]*protocol.MemoryUser, len(i.users)) - copy(dst, i.users) - return dst -} - -// GetUsersCount implements proxy.UserManager.GetUsersCount(). -func (i *MultiUserInbound) GetUsersCount(context.Context) int64 { - i.Lock() - defer i.Unlock() - return int64(len(i.users)) -} - func (i *MultiUserInbound) Network() []net.Network { return i.networks } func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { inbound := session.InboundFromContext(ctx) - inbound.Name = "shadowsocks-2022-multi" - inbound.CanSpliceCopy = 3 var metadata M.Metadata if inbound.Source.IsValid() { @@ -200,7 +162,7 @@ func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, con ctx = session.ContextWithDispatcher(ctx, dispatcher) if network == net.Network_TCP { - return singbridge.ReturnError(i.service.NewConnection(ctx, connection, metadata)) + return returnError(i.service.NewConnection(ctx, connection, metadata)) } else { reader := buf.NewReader(connection) pc := &natPacketConn{connection} @@ -208,17 +170,17 @@ func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, con mb, err := reader.ReadMultiBuffer() if err != nil { buf.ReleaseMulti(mb) - return singbridge.ReturnError(err) + return returnError(err) } for _, buffer := range mb { packet := B.As(buffer.Bytes()).ToOwned() - buffer.Release() err = i.service.NewPacket(ctx, pc, packet, metadata) if err != nil { packet.Release() buf.ReleaseMulti(mb) return err } + buffer.Release() } } } @@ -228,46 +190,52 @@ func (i *MultiUserInbound) NewConnection(ctx context.Context, conn net.Conn, met inbound := session.InboundFromContext(ctx) userInt, _ := A.UserFromContext[int](ctx) user := i.users[userInt] - inbound.User = user + inbound.User = &protocol.MemoryUser{ + Email: user.Email, + Level: uint32(user.Level), + } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: metadata.Source, To: metadata.Destination, Status: log.AccessAccepted, Email: user.Email, }) - errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination) + newError("tunnelling request to tcp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) dispatcher := session.DispatcherFromContext(ctx) - destination := singbridge.ToDestination(metadata.Destination, net.Network_TCP) - if !destination.IsValid() { - return errors.New("invalid destination") - } - - link, err := dispatcher.Dispatch(ctx, destination) + link, err := dispatcher.Dispatch(ctx, toDestination(metadata.Destination, net.Network_TCP)) if err != nil { return err } - return singbridge.CopyConn(ctx, conn, link, conn) + outConn := &pipeConnWrapper{ + &buf.BufferedReader{Reader: link.Reader}, + link.Writer, + conn, + } + return bufio.CopyConn(ctx, conn, outConn) } func (i *MultiUserInbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { inbound := session.InboundFromContext(ctx) userInt, _ := A.UserFromContext[int](ctx) user := i.users[userInt] - inbound.User = user + inbound.User = &protocol.MemoryUser{ + Email: user.Email, + Level: uint32(user.Level), + } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: metadata.Source, To: metadata.Destination, Status: log.AccessAccepted, Email: user.Email, }) - errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination) + newError("tunnelling request to udp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) dispatcher := session.DispatcherFromContext(ctx) - destination := singbridge.ToDestination(metadata.Destination, net.Network_UDP) + destination := toDestination(metadata.Destination, net.Network_UDP) link, err := dispatcher.Dispatch(ctx, destination) if err != nil { return err } - outConn := &singbridge.PacketConnWrapper{ + outConn := &packetConnWrapper{ Reader: link.Reader, Writer: link.Writer, Dest: destination, @@ -279,5 +247,5 @@ func (i *MultiUserInbound) NewError(ctx context.Context, err error) { if E.IsClosed(err) { return } - errors.LogWarning(ctx, err.Error()) + newError(err).AtWarning().WriteToLog() } diff --git a/proxy/shadowsocks_2022/inbound_relay.go b/proxy/shadowsocks_2022/inbound_relay.go index 19afd462..3e0043ee 100644 --- a/proxy/shadowsocks_2022/inbound_relay.go +++ b/proxy/shadowsocks_2022/inbound_relay.go @@ -15,12 +15,10 @@ import ( N "github.com/sagernet/sing/common/network" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/singbridge" "github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet/stat" @@ -51,11 +49,11 @@ func NewRelayServer(ctx context.Context, config *RelayServerConfig) (*RelayInbou destinations: config.Destinations, } if !C.Contains(shadowaead_2022.List, config.Method) || !strings.Contains(config.Method, "aes") { - return nil, errors.New("unsupported method ", config.Method) + return nil, newError("unsupported method ", config.Method) } service, err := shadowaead_2022.NewRelayServiceWithPassword[int](config.Method, config.Key, 500, inbound) if err != nil { - return nil, errors.New("create service").Base(err) + return nil, newError("create service").Base(err) } for i, destination := range config.Destinations { @@ -68,14 +66,14 @@ func NewRelayServer(ctx context.Context, config *RelayServerConfig) (*RelayInbou C.MapIndexed(config.Destinations, func(index int, it *RelayDestination) int { return index }), C.Map(config.Destinations, func(it *RelayDestination) string { return it.Key }), C.Map(config.Destinations, func(it *RelayDestination) M.Socksaddr { - return singbridge.ToSocksaddr(net.Destination{ + return toSocksaddr(net.Destination{ Address: it.Address.AsAddress(), Port: net.Port(it.Port), }) }), ) if err != nil { - return nil, errors.New("create service").Base(err) + return nil, newError("create service").Base(err) } inbound.service = service return inbound, nil @@ -87,8 +85,6 @@ func (i *RelayInbound) Network() []net.Network { func (i *RelayInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { inbound := session.InboundFromContext(ctx) - inbound.Name = "shadowsocks-2022-relay" - inbound.CanSpliceCopy = 3 var metadata M.Metadata if inbound.Source.IsValid() { @@ -98,7 +94,7 @@ func (i *RelayInbound) Process(ctx context.Context, network net.Network, connect ctx = session.ContextWithDispatcher(ctx, dispatcher) if network == net.Network_TCP { - return singbridge.ReturnError(i.service.NewConnection(ctx, connection, metadata)) + return returnError(i.service.NewConnection(ctx, connection, metadata)) } else { reader := buf.NewReader(connection) pc := &natPacketConn{connection} @@ -106,17 +102,17 @@ func (i *RelayInbound) Process(ctx context.Context, network net.Network, connect mb, err := reader.ReadMultiBuffer() if err != nil { buf.ReleaseMulti(mb) - return singbridge.ReturnError(err) + return returnError(err) } for _, buffer := range mb { packet := B.As(buffer.Bytes()).ToOwned() - buffer.Release() err = i.service.NewPacket(ctx, pc, packet, metadata) if err != nil { packet.Release() buf.ReleaseMulti(mb) return err } + buffer.Release() } } } @@ -136,13 +132,18 @@ func (i *RelayInbound) NewConnection(ctx context.Context, conn net.Conn, metadat Status: log.AccessAccepted, Email: user.Email, }) - errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination) + newError("tunnelling request to tcp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) dispatcher := session.DispatcherFromContext(ctx) - link, err := dispatcher.Dispatch(ctx, singbridge.ToDestination(metadata.Destination, net.Network_TCP)) + link, err := dispatcher.Dispatch(ctx, toDestination(metadata.Destination, net.Network_TCP)) if err != nil { return err } - return singbridge.CopyConn(ctx, nil, link, conn) + outConn := &pipeConnWrapper{ + &buf.BufferedReader{Reader: link.Reader}, + link.Writer, + conn, + } + return bufio.CopyConn(ctx, conn, outConn) } func (i *RelayInbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { @@ -159,14 +160,14 @@ func (i *RelayInbound) NewPacketConnection(ctx context.Context, conn N.PacketCon Status: log.AccessAccepted, Email: user.Email, }) - errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination) + newError("tunnelling request to udp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) dispatcher := session.DispatcherFromContext(ctx) - destination := singbridge.ToDestination(metadata.Destination, net.Network_UDP) + destination := toDestination(metadata.Destination, net.Network_UDP) link, err := dispatcher.Dispatch(ctx, destination) if err != nil { return err } - outConn := &singbridge.PacketConnWrapper{ + outConn := &packetConnWrapper{ Reader: link.Reader, Writer: link.Writer, Dest: destination, @@ -178,5 +179,5 @@ func (i *RelayInbound) NewError(ctx context.Context, err error) { if E.IsClosed(err) { return } - errors.LogWarning(ctx, err.Error()) + newError(err).AtWarning().WriteToLog() } diff --git a/proxy/shadowsocks_2022/outbound.go b/proxy/shadowsocks_2022/outbound.go index dd2e4259..cc23f737 100644 --- a/proxy/shadowsocks_2022/outbound.go +++ b/proxy/shadowsocks_2022/outbound.go @@ -2,21 +2,22 @@ package shadowsocks_2022 import ( "context" + "io" + "runtime" "time" - shadowsocks "github.com/sagernet/sing-shadowsocks" + "github.com/sagernet/sing-shadowsocks" "github.com/sagernet/sing-shadowsocks/shadowaead_2022" C "github.com/sagernet/sing/common" B "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" + M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/uot" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/singbridge" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" ) @@ -28,10 +29,10 @@ func init() { } type Outbound struct { - ctx context.Context - server net.Destination - method shadowsocks.Method - uotClient *uot.Client + ctx context.Context + server net.Destination + method shadowsocks.Method + uot bool } func NewClient(ctx context.Context, config *ClientConfig) (*Outbound, error) { @@ -42,21 +43,19 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Outbound, error) { Port: net.Port(config.Port), Network: net.Network_TCP, }, + uot: config.UdpOverTcp, } if C.Contains(shadowaead_2022.List, config.Method) { if config.Key == "" { - return nil, errors.New("missing psk") + return nil, newError("missing psk") } - method, err := shadowaead_2022.NewWithPassword(config.Method, config.Key, nil) + method, err := shadowaead_2022.NewWithPassword(config.Method, config.Key) if err != nil { - return nil, errors.New("create method").Base(err) + return nil, newError("create method").Base(err) } o.method = method } else { - return nil, errors.New("unknown method ", config.Method) - } - if config.UdpOverTcp { - o.uotClient = &uot.Client{Version: uint8(config.UdpOverTcpVersion)} + return nil, newError("unknown method ", config.Method) } return o, nil } @@ -68,68 +67,73 @@ func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer int inboundConn = inbound.Conn } - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified") } - ob.Name = "shadowsocks-2022" - ob.CanSpliceCopy = 3 - destination := ob.Target + destination := outbound.Target network := destination.Network - errors.LogInfo(ctx, "tunneling request to ", destination, " via ", o.server.NetAddr()) + newError("tunneling request to ", destination, " via ", o.server.NetAddr()).WriteToLog(session.ExportIDToError(ctx)) serverDestination := o.server - if o.uotClient != nil { + if o.uot { serverDestination.Network = net.Network_TCP } else { serverDestination.Network = network } connection, err := dialer.Dial(ctx, serverDestination) if err != nil { - return errors.New("failed to connect to server").Base(err) - } - - if session.TimeoutOnlyFromContext(ctx) { - ctx, _ = context.WithCancel(context.Background()) + return newError("failed to connect to server").Base(err) } if network == net.Network_TCP { - serverConn := o.method.DialEarlyConn(connection, singbridge.ToSocksaddr(destination)) + serverConn := o.method.DialEarlyConn(connection, toSocksaddr(destination)) var handshake bool if timeoutReader, isTimeoutReader := link.Reader.(buf.TimeoutReader); isTimeoutReader { mb, err := timeoutReader.ReadMultiBufferTimeout(time.Millisecond * 100) if err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { - return errors.New("read payload").Base(err) + return newError("read payload").Base(err) } - payload := B.New() + _payload := B.StackNew() + payload := C.Dup(_payload) + defer payload.Release() for { - payload.Reset() + payload.FullReset() nb, n := buf.SplitBytes(mb, payload.FreeBytes()) if n > 0 { payload.Truncate(n) _, err = serverConn.Write(payload.Bytes()) if err != nil { - payload.Release() - return errors.New("write payload").Base(err) + return newError("write payload").Base(err) } handshake = true } if nb.IsEmpty() { break + } else { + mb = nb } - mb = nb } - payload.Release() + runtime.KeepAlive(_payload) } if !handshake { _, err = serverConn.Write(nil) if err != nil { - return errors.New("client handshake").Base(err) + return newError("client handshake").Base(err) } } - return singbridge.CopyConn(ctx, inboundConn, link, serverConn) + conn := &pipeConnWrapper{ + W: link.Writer, + Conn: inboundConn, + } + if ir, ok := link.Reader.(io.Reader); ok { + conn.R = ir + } else { + conn.R = &buf.BufferedReader{Reader: link.Reader} + } + + return returnError(bufio.CopyConn(ctx, conn, serverConn)) } else { var packetConn N.PacketConn if pc, isPacketConn := inboundConn.(N.PacketConn); isPacketConn { @@ -137,7 +141,7 @@ func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer int } else if nc, isNetPacket := inboundConn.(net.PacketConn); isNetPacket { packetConn = bufio.NewPacketConn(nc) } else { - packetConn = &singbridge.PacketConnWrapper{ + packetConn = &packetConnWrapper{ Reader: link.Reader, Writer: link.Writer, Conn: inboundConn, @@ -145,15 +149,12 @@ func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer int } } - if o.uotClient != nil { - uConn, err := o.uotClient.DialEarlyConn(o.method.DialEarlyConn(connection, uot.RequestDestination(o.uotClient.Version)), false, singbridge.ToSocksaddr(destination)) - if err != nil { - return err - } - return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, uConn)) + if o.uot { + serverConn := o.method.DialEarlyConn(connection, M.Socksaddr{Fqdn: uot.UOTMagicAddress}) + return returnError(bufio.CopyPacketConn(ctx, packetConn, uot.NewClientConn(serverConn))) } else { serverConn := o.method.DialPacketConn(connection) - return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, serverConn)) + return returnError(bufio.CopyPacketConn(ctx, packetConn, serverConn)) } } } diff --git a/proxy/shadowsocks_2022/shadowsocks_2022.go b/proxy/shadowsocks_2022/shadowsocks_2022.go index 96f62c74..945c4499 100644 --- a/proxy/shadowsocks_2022/shadowsocks_2022.go +++ b/proxy/shadowsocks_2022/shadowsocks_2022.go @@ -1 +1,145 @@ package shadowsocks_2022 + +import ( + "io" + + B "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" +) + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + +func toDestination(socksaddr M.Socksaddr, network net.Network) net.Destination { + if socksaddr.IsFqdn() { + return net.Destination{ + Network: network, + Address: net.DomainAddress(socksaddr.Fqdn), + Port: net.Port(socksaddr.Port), + } + } else { + return net.Destination{ + Network: network, + Address: net.IPAddress(socksaddr.Addr.AsSlice()), + Port: net.Port(socksaddr.Port), + } + } +} + +func toSocksaddr(destination net.Destination) M.Socksaddr { + var addr M.Socksaddr + switch destination.Address.Family() { + case net.AddressFamilyDomain: + addr.Fqdn = destination.Address.Domain() + default: + addr.Addr = M.AddrFromIP(destination.Address.IP()) + } + addr.Port = uint16(destination.Port) + return addr +} + +type pipeConnWrapper struct { + R io.Reader + W buf.Writer + net.Conn +} + +func (w *pipeConnWrapper) Close() error { + return nil +} + +func (w *pipeConnWrapper) Read(b []byte) (n int, err error) { + return w.R.Read(b) +} + +func (w *pipeConnWrapper) Write(p []byte) (n int, err error) { + n = len(p) + var mb buf.MultiBuffer + pLen := len(p) + for pLen > 0 { + buffer := buf.New() + if pLen > buf.Size { + _, err = buffer.Write(p[:buf.Size]) + p = p[buf.Size:] + } else { + buffer.Write(p) + } + pLen -= int(buffer.Len()) + mb = append(mb, buffer) + } + err = w.W.WriteMultiBuffer(mb) + if err != nil { + n = 0 + buf.ReleaseMulti(mb) + } + return +} + +type packetConnWrapper struct { + buf.Reader + buf.Writer + net.Conn + Dest net.Destination + cached buf.MultiBuffer +} + +func (w *packetConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) { + if w.cached != nil { + mb, bb := buf.SplitFirst(w.cached) + if bb == nil { + w.cached = nil + } else { + buffer.Write(bb.Bytes()) + w.cached = mb + var destination net.Destination + if bb.UDP != nil { + destination = *bb.UDP + } else { + destination = w.Dest + } + bb.Release() + return toSocksaddr(destination), nil + } + } + mb, err := w.ReadMultiBuffer() + if err != nil { + return M.Socksaddr{}, err + } + nb, bb := buf.SplitFirst(mb) + if bb == nil { + return M.Socksaddr{}, nil + } else { + buffer.Write(bb.Bytes()) + w.cached = nb + var destination net.Destination + if bb.UDP != nil { + destination = *bb.UDP + } else { + destination = w.Dest + } + bb.Release() + return toSocksaddr(destination), nil + } +} + +func (w *packetConnWrapper) WritePacket(buffer *B.Buffer, destination M.Socksaddr) error { + vBuf := buf.New() + vBuf.Write(buffer.Bytes()) + endpoint := toDestination(destination, net.Network_UDP) + vBuf.UDP = &endpoint + return w.Writer.WriteMultiBuffer(buf.MultiBuffer{vBuf}) +} + +func (w *packetConnWrapper) Close() error { + buf.ReleaseMulti(w.cached) + return nil +} + +func returnError(err error) error { + if E.IsClosed(err) { + return nil + } + return err +} diff --git a/proxy/socks/client.go b/proxy/socks/client.go index 232215ec..f1690bec 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -6,7 +6,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/retry" @@ -14,6 +13,7 @@ import ( "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" @@ -24,6 +24,8 @@ import ( type Client struct { serverPicker protocol.ServerPicker policyManager policy.Manager + version Version + dns dns.Client } // NewClient create a new Socks5 client based on the given config. @@ -32,18 +34,22 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { - return nil, errors.New("failed to get server spec").Base(err) + return nil, newError("failed to get server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { - return nil, errors.New("0 target server") + return nil, newError("0 target server") } v := core.MustFromContext(ctx) c := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + version: config.Version, + } + if config.Version == Version_SOCKS4 { + c.dns = v.GetFeature(dns.ClientType()).(dns.Client) } return c, nil @@ -51,15 +57,12 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { // Process implements proxy.Outbound.Process. func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified.") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified.") } - ob.Name = "socks" - ob.CanSpliceCopy = 2 // Destination of the inner request. - destination := ob.Target + destination := outbound.Target // Outbound server. var server *protocol.ServerSpec @@ -79,12 +82,12 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return nil }); err != nil { - return errors.New("failed to find an available destination").Base(err) + return newError("failed to find an available destination").Base(err) } defer func() { if err := conn.Close(); err != nil { - errors.LogInfoInner(ctx, err, "failed to closed connection") + newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } }() @@ -97,6 +100,30 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter Port: destination.Port, } + switch c.version { + case Version_SOCKS4: + if request.Address.Family().IsDomain() { + ips, err := c.dns.LookupIP(request.Address.Domain(), dns.IPOption{ + IPv4Enable: true, + }) + if err != nil { + return err + } else if len(ips) == 0 { + return dns.ErrEmptyResponse + } + request.Address = net.IPAddress(ips[0]) + } + fallthrough + case Version_SOCKS4A: + request.Version = socks4Version + + if destination.Network == net.Network_UDP { + return newError("udp is not supported in socks4") + } else if destination.Address.Family().IsIPv6() { + return newError("ipv6 is not supported in socks4") + } + } + if destination.Network == net.Network_UDP { request.Command = protocol.RequestCommandUDP } @@ -108,11 +135,11 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter } if err := conn.SetDeadline(time.Now().Add(p.Timeouts.Handshake)); err != nil { - errors.LogInfoInner(ctx, err, "failed to set deadline for handshake") + newError("failed to set deadline for handshake").Base(err).WriteToLog(session.ExportIDToError(ctx)) } udpRequest, err := ClientHandshake(request, conn, conn) if err != nil { - return errors.New("failed to establish connection to server").AtWarning().Base(err) + return newError("failed to establish connection to server").AtWarning().Base(err) } if udpRequest != nil { if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 { @@ -121,22 +148,11 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter } if err := conn.SetDeadline(time.Time{}); err != nil { - errors.LogInfoInner(ctx, err, "failed to clear deadline after handshake") - } - - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) + newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx)) } ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, p.Timeouts.ConnectionIdle) + timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle) var requestFunc func() error var responseFunc func() error @@ -146,14 +162,13 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) } responseFunc = func() error { - ob.CanSpliceCopy = 1 defer timer.SetTimeout(p.Timeouts.UplinkOnly) return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) } } else if request.Command == protocol.RequestCommandUDP { udpConn, err := dialer.Dial(ctx, udpRequest.Destination()) if err != nil { - return errors.New("failed to create UDP connection").Base(err) + return newError("failed to create UDP connection").Base(err) } defer udpConn.Close() requestFunc = func() error { @@ -162,20 +177,15 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)) } responseFunc = func() error { - ob.CanSpliceCopy = 1 defer timer.SetTimeout(p.Timeouts.UplinkOnly) reader := &UDPReader{Reader: udpConn} return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)) } } - if newCtx != nil { - ctx = newCtx - } - responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil diff --git a/proxy/socks/config.go b/proxy/socks/config.go index e8aa328a..3b162abe 100644 --- a/proxy/socks/config.go +++ b/proxy/socks/config.go @@ -1,10 +1,6 @@ package socks -import ( - "google.golang.org/protobuf/proto" - - "github.com/xtls/xray-core/common/protocol" -) +import "github.com/xtls/xray-core/common/protocol" func (a *Account) Equals(another protocol.Account) bool { if account, ok := another.(*Account); ok { @@ -13,10 +9,6 @@ func (a *Account) Equals(another protocol.Account) bool { return false } -func (a *Account) ToProto() proto.Message { - return a -} - func (a *Account) AsAccount() (protocol.Account, error) { return a, nil } diff --git a/proxy/socks/config.pb.go b/proxy/socks/config.pb.go index ee94d0bc..3cb7d172 100644 --- a/proxy/socks/config.pb.go +++ b/proxy/socks/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/socks/config.proto package socks @@ -26,7 +26,7 @@ const ( type AuthType int32 const ( - // NO_AUTH is for anonymous authentication. + // NO_AUTH is for anounymous authentication. AuthType_NO_AUTH AuthType = 0 // PASSWORD is for username/password authentication. AuthType_PASSWORD AuthType = 1 @@ -71,6 +71,55 @@ func (AuthType) EnumDescriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{0} } +type Version int32 + +const ( + Version_SOCKS5 Version = 0 + Version_SOCKS4 Version = 1 + Version_SOCKS4A Version = 2 +) + +// Enum value maps for Version. +var ( + Version_name = map[int32]string{ + 0: "SOCKS5", + 1: "SOCKS4", + 2: "SOCKS4A", + } + Version_value = map[string]int32{ + "SOCKS5": 0, + "SOCKS4": 1, + "SOCKS4A": 2, + } +) + +func (x Version) Enum() *Version { + p := new(Version) + *p = x + return p +} + +func (x Version) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Version) Descriptor() protoreflect.EnumDescriptor { + return file_proxy_socks_config_proto_enumTypes[1].Descriptor() +} + +func (Version) Type() protoreflect.EnumType { + return &file_proxy_socks_config_proto_enumTypes[1] +} + +func (x Version) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Version.Descriptor instead. +func (Version) EnumDescriptor() ([]byte, []int) { + return file_proxy_socks_config_proto_rawDescGZIP(), []int{1} +} + // Account represents a Socks account. type Account struct { state protoimpl.MessageState @@ -83,9 +132,11 @@ type Account struct { func (x *Account) Reset() { *x = Account{} - mi := &file_proxy_socks_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_socks_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Account) String() string { @@ -96,7 +147,7 @@ func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -135,14 +186,18 @@ type ServerConfig struct { Accounts map[string]string `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Address *net.IPOrDomain `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` UdpEnabled bool `protobuf:"varint,4,opt,name=udp_enabled,json=udpEnabled,proto3" json:"udp_enabled,omitempty"` - UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` + // Deprecated: Do not use. + Timeout uint32 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` + UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` } func (x *ServerConfig) Reset() { *x = ServerConfig{} - mi := &file_proxy_socks_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_socks_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ServerConfig) String() string { @@ -153,7 +208,7 @@ func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -196,6 +251,14 @@ func (x *ServerConfig) GetUdpEnabled() bool { return false } +// Deprecated: Do not use. +func (x *ServerConfig) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + func (x *ServerConfig) GetUserLevel() uint32 { if x != nil { return x.UserLevel @@ -210,14 +273,17 @@ type ClientConfig struct { unknownFields protoimpl.UnknownFields // Sever is a list of Socks server addresses. - Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` + Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` + Version Version `protobuf:"varint,2,opt,name=version,proto3,enum=xray.proxy.socks.Version" json:"version,omitempty"` } func (x *ClientConfig) Reset() { *x = ClientConfig{} - mi := &file_proxy_socks_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_socks_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ClientConfig) String() string { @@ -228,7 +294,7 @@ func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -250,6 +316,13 @@ func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { return nil } +func (x *ClientConfig) GetVersion() Version { + if x != nil { + return x.Version + } + return Version_SOCKS5 +} + var File_proxy_socks_config_proto protoreflect.FileDescriptor var file_proxy_socks_config_proto_rawDesc = []byte{ @@ -263,7 +336,7 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xc5, 0x02, 0x0a, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xe3, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, @@ -278,26 +351,34 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x64, 0x70, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x64, - 0x70, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, - 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4c, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2a, 0x25, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, - 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, - 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, - 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, 0x63, 0x6b, - 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, 0x70, - 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, - 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x81, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x12, 0x33, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x25, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x10, 0x00, 0x12, + 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x2a, 0x2e, 0x0a, + 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, + 0x53, 0x35, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, 0x10, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, 0x41, 0x10, 0x02, 0x42, 0x52, 0x0a, + 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x73, 0x6f, 0x63, 0x6b, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, + 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x6f, 0x63, 0x6b, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -312,27 +393,29 @@ func file_proxy_socks_config_proto_rawDescGZIP() []byte { return file_proxy_socks_config_proto_rawDescData } -var file_proxy_socks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_proxy_socks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_proxy_socks_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_proxy_socks_config_proto_goTypes = []any{ +var file_proxy_socks_config_proto_goTypes = []interface{}{ (AuthType)(0), // 0: xray.proxy.socks.AuthType - (*Account)(nil), // 1: xray.proxy.socks.Account - (*ServerConfig)(nil), // 2: xray.proxy.socks.ServerConfig - (*ClientConfig)(nil), // 3: xray.proxy.socks.ClientConfig - nil, // 4: xray.proxy.socks.ServerConfig.AccountsEntry - (*net.IPOrDomain)(nil), // 5: xray.common.net.IPOrDomain - (*protocol.ServerEndpoint)(nil), // 6: xray.common.protocol.ServerEndpoint + (Version)(0), // 1: xray.proxy.socks.Version + (*Account)(nil), // 2: xray.proxy.socks.Account + (*ServerConfig)(nil), // 3: xray.proxy.socks.ServerConfig + (*ClientConfig)(nil), // 4: xray.proxy.socks.ClientConfig + nil, // 5: xray.proxy.socks.ServerConfig.AccountsEntry + (*net.IPOrDomain)(nil), // 6: xray.common.net.IPOrDomain + (*protocol.ServerEndpoint)(nil), // 7: xray.common.protocol.ServerEndpoint } var file_proxy_socks_config_proto_depIdxs = []int32{ 0, // 0: xray.proxy.socks.ServerConfig.auth_type:type_name -> xray.proxy.socks.AuthType - 4, // 1: xray.proxy.socks.ServerConfig.accounts:type_name -> xray.proxy.socks.ServerConfig.AccountsEntry - 5, // 2: xray.proxy.socks.ServerConfig.address:type_name -> xray.common.net.IPOrDomain - 6, // 3: xray.proxy.socks.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 5, // 1: xray.proxy.socks.ServerConfig.accounts:type_name -> xray.proxy.socks.ServerConfig.AccountsEntry + 6, // 2: xray.proxy.socks.ServerConfig.address:type_name -> xray.common.net.IPOrDomain + 7, // 3: xray.proxy.socks.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint + 1, // 4: xray.proxy.socks.ClientConfig.version:type_name -> xray.proxy.socks.Version + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_proxy_socks_config_proto_init() } @@ -340,12 +423,50 @@ func file_proxy_socks_config_proto_init() { if File_proxy_socks_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_socks_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_socks_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_socks_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proxy_socks_config_proto_rawDesc, - NumEnums: 1, + NumEnums: 2, NumMessages: 4, NumExtensions: 0, NumServices: 0, diff --git a/proxy/socks/config.proto b/proxy/socks/config.proto index 32d553d3..7e7dc09f 100644 --- a/proxy/socks/config.proto +++ b/proxy/socks/config.proto @@ -17,18 +17,25 @@ message Account { // AuthType is the authentication type of Socks proxy. enum AuthType { - // NO_AUTH is for anonymous authentication. + // NO_AUTH is for anounymous authentication. NO_AUTH = 0; // PASSWORD is for username/password authentication. PASSWORD = 1; } +enum Version { + SOCKS5 = 0; + SOCKS4 = 1; + SOCKS4A = 2; +} + // ServerConfig is the protobuf config for Socks server. message ServerConfig { AuthType auth_type = 1; map accounts = 2; xray.common.net.IPOrDomain address = 3; bool udp_enabled = 4; + uint32 timeout = 5 [deprecated = true]; uint32 user_level = 6; } @@ -36,4 +43,5 @@ message ServerConfig { message ClientConfig { // Sever is a list of Socks server addresses. repeated xray.common.protocol.ServerEndpoint server = 1; + Version version = 2; } diff --git a/proxy/socks/errors.generated.go b/proxy/socks/errors.generated.go new file mode 100644 index 00000000..f466011d --- /dev/null +++ b/proxy/socks/errors.generated.go @@ -0,0 +1,9 @@ +package socks + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 9bccf607..3f371d57 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -6,7 +6,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" ) @@ -49,7 +48,7 @@ type ServerSession struct { func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { if s.config.AuthType == AuthType_PASSWORD { writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0)) - return nil, errors.New("socks 4 is not allowed when auth is required.") + return nil, newError("socks 4 is not allowed when auth is required.") } var port net.Port @@ -59,7 +58,7 @@ func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) buffer := buf.StackNew() if _, err := buffer.ReadFullFrom(reader, 6); err != nil { buffer.Release() - return nil, errors.New("insufficient header").Base(err) + return nil, newError("insufficient header").Base(err) } port = net.PortFromBytes(buffer.BytesRange(0, 2)) address = net.IPAddress(buffer.BytesRange(2, 6)) @@ -72,9 +71,9 @@ func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) if address.IP()[0] == 0x00 { domain, err := ReadUntilNull(reader) if err != nil { - return nil, errors.New("failed to read domain for socks 4a").Base(err) + return nil, newError("failed to read domain for socks 4a").Base(err) } - address = net.ParseAddress(domain) + address = net.DomainAddress(domain) } switch cmd { @@ -91,7 +90,7 @@ func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) return request, nil default: writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0)) - return nil, errors.New("unsupported command: ", cmd) + return nil, newError("unsupported command: ", cmd) } } @@ -100,7 +99,7 @@ func (s *ServerSession) auth5(nMethod byte, reader io.Reader, writer io.Writer) defer buffer.Release() if _, err = buffer.ReadFullFrom(reader, int32(nMethod)); err != nil { - return "", errors.New("failed to read auth methods").Base(err) + return "", newError("failed to read auth methods").Base(err) } var expectedAuth byte = authNotRequired @@ -110,26 +109,26 @@ func (s *ServerSession) auth5(nMethod byte, reader io.Reader, writer io.Writer) if !hasAuthMethod(expectedAuth, buffer.BytesRange(0, int32(nMethod))) { writeSocks5AuthenticationResponse(writer, socks5Version, authNoMatchingMethod) - return "", errors.New("no matching auth method") + return "", newError("no matching auth method") } if err := writeSocks5AuthenticationResponse(writer, socks5Version, expectedAuth); err != nil { - return "", errors.New("failed to write auth response").Base(err) + return "", newError("failed to write auth response").Base(err) } if expectedAuth == authPassword { username, password, err := ReadUsernamePassword(reader) if err != nil { - return "", errors.New("failed to read username and password for authentication").Base(err) + return "", newError("failed to read username and password for authentication").Base(err) } if !s.config.HasAccount(username, password) { writeSocks5AuthenticationResponse(writer, 0x01, 0xFF) - return "", errors.New("invalid username or password") + return "", newError("invalid username or password") } if err := writeSocks5AuthenticationResponse(writer, 0x01, 0x00); err != nil { - return "", errors.New("failed to write auth response").Base(err) + return "", newError("failed to write auth response").Base(err) } return username, nil } @@ -151,7 +150,7 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri buffer := buf.StackNew() if _, err := buffer.ReadFullFrom(reader, 3); err != nil { buffer.Release() - return nil, errors.New("failed to read request").Base(err) + return nil, newError("failed to read request").Base(err) } cmd = buffer.Byte(1) buffer.Release() @@ -168,22 +167,22 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri case cmdUDPAssociate: if !s.config.UdpEnabled { writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) - return nil, errors.New("UDP is not enabled.") + return nil, newError("UDP is not enabled.") } request.Command = protocol.RequestCommandUDP case cmdTCPBind: writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) - return nil, errors.New("TCP bind is not supported.") + return nil, newError("TCP bind is not supported.") default: writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) - return nil, errors.New("unknown command ", cmd) + return nil, newError("unknown command ", cmd) } request.Version = socks5Version addr, port, err := addrParser.ReadAddressPort(nil, reader) if err != nil { - return nil, errors.New("failed to read address").Base(err) + return nil, newError("failed to read address").Base(err) } request.Address = addr request.Port = port @@ -212,7 +211,7 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol buffer := buf.StackNew() if _, err := buffer.ReadFullFrom(reader, 2); err != nil { buffer.Release() - return nil, errors.New("insufficient header").Base(err) + return nil, newError("insufficient header").Base(err) } version := buffer.Byte(0) @@ -225,7 +224,7 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol case socks5Version: return s.handshake5(cmd, reader, writer) default: - return nil, errors.New("unknown Socks version: ", version) + return nil, newError("unknown Socks version: ", version) } } @@ -279,7 +278,7 @@ func ReadUntilNull(reader io.Reader) (string, error) { return b.String(), nil } if b.IsFull() { - return "", errors.New("buffer overrun") + return "", newError("buffer overrun") } } } @@ -323,7 +322,7 @@ func writeSocks4Response(writer io.Writer, errCode byte, address net.Address, po func DecodeUDPPacket(packet *buf.Buffer) (*protocol.RequestHeader, error) { if packet.Len() < 5 { - return nil, errors.New("insufficient length of packet.") + return nil, newError("insufficient length of packet.") } request := &protocol.RequestHeader{ Version: socks5Version, @@ -332,14 +331,14 @@ func DecodeUDPPacket(packet *buf.Buffer) (*protocol.RequestHeader, error) { // packet[0] and packet[1] are reserved if packet.Byte(2) != 0 /* fragments */ { - return nil, errors.New("discarding fragmented payload.") + return nil, newError("discarding fragmented payload.") } packet.Advance(3) addr, port, err := addrParser.ReadAddressPort(nil, packet) if err != nil { - return nil, errors.New("failed to read UDP header").Base(err) + return nil, newError("failed to read UDP header").Base(err) } request.Address = addr request.Port = port @@ -433,10 +432,10 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i } if b.Byte(0) != socks5Version { - return nil, errors.New("unexpected server version: ", b.Byte(0)).AtWarning() + return nil, newError("unexpected server version: ", b.Byte(0)).AtWarning() } if b.Byte(1) != authByte { - return nil, errors.New("auth method not supported.").AtWarning() + return nil, newError("auth method not supported.").AtWarning() } if authByte == authPassword { @@ -456,7 +455,7 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i return nil, err } if b.Byte(1) != 0x00 { - return nil, errors.New("server rejects account: ", b.Byte(1)) + return nil, newError("server rejects account: ", b.Byte(1)) } } @@ -486,7 +485,7 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i resp := b.Byte(1) if resp != 0x00 { - return nil, errors.New("server rejects request: ", resp) + return nil, newError("server rejects request: ", resp) } b.Clear() diff --git a/proxy/socks/protocol_test.go b/proxy/socks/protocol_test.go index b8c84f68..99e07389 100644 --- a/proxy/socks/protocol_test.go +++ b/proxy/socks/protocol_test.go @@ -68,7 +68,7 @@ func TestReadUsernamePassword(t *testing.T) { t.Error("for input: ", testCase.Input, " expect username ", testCase.Username, " but actually ", username) } if testCase.Password != password { - t.Error("for input: ", testCase.Input, " expect password ", testCase.Password, " but actually ", password) + t.Error("for input: ", testCase.Input, " expect passowrd ", testCase.Password, " but actually ", password) } } } diff --git a/proxy/socks/server.go b/proxy/socks/server.go index dd6f3953..ce15163c 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -2,13 +2,11 @@ package socks import ( "context" - goerrors "errors" "io" "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" @@ -17,9 +15,9 @@ import ( "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/proxy/http" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/udp" ) @@ -29,8 +27,6 @@ type Server struct { config *ServerConfig policyManager policy.Manager cone bool - udpFilter *UDPFilter - httpServer *http.Server } // NewServer creates a new Server object. @@ -41,20 +37,18 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), cone: ctx.Value("cone").(bool), } - httpConfig := &http.ServerConfig{ - UserLevel: config.UserLevel, - } - if config.AuthType == AuthType_PASSWORD { - httpConfig.Accounts = config.Accounts - s.udpFilter = new(UDPFilter) // We only use this when auth is enabled - } - s.httpServer, _ = http.NewServer(ctx, httpConfig) return s, nil } func (s *Server) policy() policy.Session { config := s.config p := s.policyManager.ForLevel(config.UserLevel) + if config.Timeout > 0 { + features.PrintDeprecatedFeatureWarning("Socks timeout") + } + if config.Timeout > 0 && config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second + } return p } @@ -69,44 +63,31 @@ func (s *Server) Network() []net.Network { // Process implements proxy.Inbound. func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { - inbound := session.InboundFromContext(ctx) - inbound.Name = "socks" - inbound.CanSpliceCopy = 2 - inbound.User = &protocol.MemoryUser{ - Level: s.config.UserLevel, + if inbound := session.InboundFromContext(ctx); inbound != nil { + inbound.User = &protocol.MemoryUser{ + Level: s.config.UserLevel, + } } switch network { case net.Network_TCP: - firstbyte := make([]byte, 1) - if n, err := conn.Read(firstbyte); n == 0 { - if goerrors.Is(err, io.EOF) { - errors.LogInfo(ctx, "Connection closed immediately, likely health check connection") - return nil - } - return errors.New("failed to read from connection").Base(err) - } - if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if it is Socks5/4/4a - errors.LogDebug(ctx, "Not Socks request, try to parse as HTTP request") - return s.httpServer.ProcessWithFirstbyte(ctx, network, conn, dispatcher, firstbyte...) - } - return s.processTCP(ctx, conn, dispatcher, firstbyte) + return s.processTCP(ctx, conn, dispatcher) case net.Network_UDP: return s.handleUDPPayload(ctx, conn, dispatcher) default: - return errors.New("unknown network: ", network) + return newError("unknown network: ", network) } } -func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher, firstbyte []byte) error { +func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error { plcy := s.policy() if err := conn.SetReadDeadline(time.Now().Add(plcy.Timeouts.Handshake)); err != nil { - errors.LogInfoInner(ctx, err, "failed to set deadline") + newError("failed to set deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } inbound := session.InboundFromContext(ctx) if inbound == nil || !inbound.Gateway.IsValid() { - return errors.New("inbound gateway not specified") + return newError("inbound gateway not specified") } svrSession := &ServerSession{ @@ -116,16 +97,10 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche localAddress: net.IPAddress(conn.LocalAddr().(*net.TCPAddr).IP), } - // Firstbyte is for forwarded conn from SOCKS inbound - // Because it needs first byte to choose protocol - // We need to add it back - reader := &buf.BufferedReader{ - Reader: buf.NewReader(conn), - Buffer: buf.MultiBuffer{buf.FromBytes(firstbyte)}, - } + reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} request, err := svrSession.Handshake(reader, conn) if err != nil { - if inbound.Source.IsValid() { + if inbound != nil && inbound.Source.IsValid() { log.Record(&log.AccessMessage{ From: inbound.Source, To: "", @@ -133,20 +108,20 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche Reason: err, }) } - return errors.New("failed to read request").Base(err) + return newError("failed to read request").Base(err) } if request.User != nil { inbound.User.Email = request.User.Email } if err := conn.SetReadDeadline(time.Time{}); err != nil { - errors.LogInfoInner(ctx, err, "failed to clear deadline") + newError("failed to clear deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } if request.Command == protocol.RequestCommandTCP { dest := request.Destination() - errors.LogInfo(ctx, "TCP Connect request to ", dest) - if inbound.Source.IsValid() { + newError("TCP Connect request to ", dest).WriteToLog(session.ExportIDToError(ctx)) + if inbound != nil && inbound.Source.IsValid() { ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: inbound.Source, To: dest, @@ -159,9 +134,6 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche } if request.Command == protocol.RequestCommandUDP { - if s.udpFilter != nil { - s.udpFilter.Add(conn.RemoteAddr()) - } return s.handleUDP(conn) } @@ -192,19 +164,18 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ requestDone := func() error { defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) if err := buf.Copy(buf.NewReader(reader), link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all TCP request").Base(err) + return newError("failed to transport all TCP request").Base(err) } return nil } responseDone := func() error { - inbound.CanSpliceCopy = 1 defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) v2writer := buf.NewWriter(writer) if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all TCP response").Base(err) + return newError("failed to transport all TCP response").Base(err) } return nil @@ -214,20 +185,16 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil } func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error { - if s.udpFilter != nil && !s.udpFilter.Check(conn.RemoteAddr()) { - errors.LogDebug(ctx, "Unauthorized UDP access from ", conn.RemoteAddr().String()) - return nil - } udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) { payload := packet.Payload - errors.LogDebug(ctx, "writing back UDP response with ", payload.Len(), " bytes") + newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx)) request := protocol.RequestHeaderFromContext(ctx) if request == nil { @@ -247,7 +214,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis defer udpMessage.Release() if err != nil { - errors.LogWarningInner(ctx, err, "failed to write UDP response") + newError("failed to write UDP response").AtWarning().Base(err).WriteToLog(session.ExportIDToError(ctx)) } conn.Write(udpMessage.Bytes()) @@ -255,9 +222,8 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis inbound := session.InboundFromContext(ctx) if inbound != nil && inbound.Source.IsValid() { - errors.LogInfo(ctx, "client UDP connection from ", inbound.Source) + newError("client UDP connection from ", inbound.Source).WriteToLog(session.ExportIDToError(ctx)) } - inbound.CanSpliceCopy = 1 var dest *net.Destination @@ -271,7 +237,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis for _, payload := range mpayload { request, err := DecodeUDPPacket(payload) if err != nil { - errors.LogInfoInner(ctx, err, "failed to parse UDP request") + newError("failed to parse UDP request").Base(err).WriteToLog(session.ExportIDToError(ctx)) payload.Release() continue } @@ -284,7 +250,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis destination := request.Destination() currentPacketCtx := ctx - errors.LogDebug(ctx, "send packet to ", destination, " with ", payload.Len(), " bytes") + newError("send packet to ", destination, " with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx)) if inbound != nil && inbound.Source.IsValid() { currentPacketCtx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: inbound.Source, diff --git a/proxy/socks/socks.go b/proxy/socks/socks.go index 2f885d39..557609c7 100644 --- a/proxy/socks/socks.go +++ b/proxy/socks/socks.go @@ -1,2 +1,4 @@ // Package socks provides implements of Socks protocol 4, 4a and 5. package socks + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/proxy/socks/udpfilter.go b/proxy/socks/udpfilter.go deleted file mode 100644 index 9ae3e697..00000000 --- a/proxy/socks/udpfilter.go +++ /dev/null @@ -1,31 +0,0 @@ -package socks - -import ( - "net" - "sync" -) - -/* -In the sock implementation of * ray, UDP authentication is flawed and can be bypassed. -Tracking a UDP connection may be a bit troublesome. -Here is a simple solution. -We create a filter, add remote IP to the pool when it try to establish a UDP connection with auth. -And drop UDP packets from unauthorized IP. -After discussion, we believe it is not necessary to add a timeout mechanism to this filter. -*/ - -type UDPFilter struct { - ips sync.Map -} - -func (f *UDPFilter) Add(addr net.Addr) bool { - ip, _, _ := net.SplitHostPort(addr.String()) - f.ips.Store(ip, true) - return true -} - -func (f *UDPFilter) Check(addr net.Addr) bool { - ip, _, _ := net.SplitHostPort(addr.String()) - _, ok := f.ips.Load(ip) - return ok -} diff --git a/proxy/trojan/client.go b/proxy/trojan/client.go index a667d352..ffd10359 100644 --- a/proxy/trojan/client.go +++ b/proxy/trojan/client.go @@ -32,12 +32,12 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { - return nil, errors.New("failed to parse server spec").Base(err) + return nil, newError("failed to parse server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { - return nil, errors.New("0 server") + return nil, newError("0 server") } v := core.MustFromContext(ctx) @@ -50,14 +50,11 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { // Process implements OutboundHandler.Process(). func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified") + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified") } - ob.Name = "trojan" - ob.CanSpliceCopy = 3 - destination := ob.Target + destination := outbound.Target network := destination.Network var server *protocol.ServerSpec @@ -74,43 +71,40 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return nil }) if err != nil { - return errors.New("failed to find an available destination").AtWarning().Base(err) + return newError("failed to find an available destination").AtWarning().Base(err) } - errors.LogInfo(ctx, "tunneling request to ", destination, " via ", server.Destination().NetAddr()) + newError("tunneling request to ", destination, " via ", server.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx)) defer conn.Close() + iConn := conn + statConn, ok := iConn.(*stat.CounterConnection) + if ok { + iConn = statConn.Connection + } + user := server.PickUser() account, ok := user.Account.(*MemoryAccount) if !ok { - return errors.New("user account is not valid") + return newError("user account is not valid") } - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) + connWriter := &ConnWriter{ + Flow: account.Flow, } sessionPolicy := c.policyManager.ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, sessionPolicy.Timeouts.ConnectionIdle) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) - connWriter := &ConnWriter{ - Writer: bufferWriter, - Target: destination, - Account: account, - } + connWriter.Writer = bufferWriter + connWriter.Target = destination + connWriter.Account = account var bodyWriter buf.Writer if destination.Network == net.Network_UDP { @@ -121,12 +115,12 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter // write some request payload to buffer if err = buf.CopyOnceTimeout(link.Reader, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { - return errors.New("failed to write A request payload").Base(err).AtWarning() + return newError("failed to write A request payload").Base(err).AtWarning() } - // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer + // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer if err = bufferWriter.SetBuffered(false); err != nil { - return errors.New("failed to flush payload").Base(err).AtWarning() + return newError("failed to flush payload").Base(err).AtWarning() } // Send header if not sent yet @@ -135,7 +129,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter } if err = buf.Copy(link.Reader, bodyWriter, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transfer request payload").Base(err).AtInfo() + return newError("failed to transfer request payload").Base(err).AtInfo() } return nil @@ -155,13 +149,9 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)) } - if newCtx != nil { - ctx = newCtx - } - responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer)) if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil diff --git a/proxy/trojan/config.go b/proxy/trojan/config.go index b7591996..ffac7854 100644 --- a/proxy/trojan/config.go +++ b/proxy/trojan/config.go @@ -3,8 +3,7 @@ package trojan import ( "crypto/sha256" "encoding/hex" - "fmt" - "google.golang.org/protobuf/proto" + fmt "fmt" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/protocol" @@ -14,6 +13,7 @@ import ( type MemoryAccount struct { Password string Key []byte + Flow string } // AsAccount implements protocol.AsAccount. @@ -23,6 +23,7 @@ func (a *Account) AsAccount() (protocol.Account, error) { return &MemoryAccount{ Password: password, Key: key, + Flow: a.Flow, }, nil } @@ -34,12 +35,6 @@ func (a *MemoryAccount) Equals(another protocol.Account) bool { return false } -func (a *MemoryAccount) ToProto() proto.Message { - return &Account{ - Password: a.Password, - } -} - func hexSha224(password string) []byte { buf := make([]byte, 56) hash := sha256.New224() diff --git a/proxy/trojan/config.pb.go b/proxy/trojan/config.pb.go index dd555ea4..17b485da 100644 --- a/proxy/trojan/config.pb.go +++ b/proxy/trojan/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/trojan/config.proto package trojan @@ -27,13 +27,16 @@ type Account struct { unknownFields protoimpl.UnknownFields Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` + Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` } func (x *Account) Reset() { *x = Account{} - mi := &file_proxy_trojan_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_trojan_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Account) String() string { @@ -44,7 +47,7 @@ func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -66,6 +69,13 @@ func (x *Account) GetPassword() string { return "" } +func (x *Account) GetFlow() string { + if x != nil { + return x.Flow + } + return "" +} + type Fallback struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -81,9 +91,11 @@ type Fallback struct { func (x *Fallback) Reset() { *x = Fallback{} - mi := &file_proxy_trojan_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_trojan_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Fallback) String() string { @@ -94,7 +106,7 @@ func (*Fallback) ProtoMessage() {} func (x *Fallback) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -161,9 +173,11 @@ type ClientConfig struct { func (x *ClientConfig) Reset() { *x = ClientConfig{} - mi := &file_proxy_trojan_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_trojan_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ClientConfig) String() string { @@ -174,7 +188,7 @@ func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -202,14 +216,16 @@ type ServerConfig struct { unknownFields protoimpl.UnknownFields Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` - Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` + Fallbacks []*Fallback `protobuf:"bytes,3,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` } func (x *ServerConfig) Reset() { *x = ServerConfig{} - mi := &file_proxy_trojan_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_trojan_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ServerConfig) String() string { @@ -220,7 +236,7 @@ func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -258,37 +274,38 @@ var file_proxy_trojan_config_proto_rawDesc = []byte{ 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, + 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x39, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, - 0x77, 0x6f, 0x72, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x08, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, - 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, 0x72, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0x4c, 0x0a, 0x0c, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, - 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x7b, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x39, 0x0a, 0x09, 0x66, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, - 0x2e, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x73, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2f, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x54, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x77, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0x82, 0x01, 0x0a, 0x08, 0x46, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x6c, 0x70, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0x4c, 0x0a, + 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, + 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x7b, 0x0a, 0x0c, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x75, + 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x39, 0x0a, + 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x74, 0x72, + 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x09, 0x66, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 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, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, + 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x54, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -304,7 +321,7 @@ func file_proxy_trojan_config_proto_rawDescGZIP() []byte { } var file_proxy_trojan_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_proxy_trojan_config_proto_goTypes = []any{ +var file_proxy_trojan_config_proto_goTypes = []interface{}{ (*Account)(nil), // 0: xray.proxy.trojan.Account (*Fallback)(nil), // 1: xray.proxy.trojan.Fallback (*ClientConfig)(nil), // 2: xray.proxy.trojan.ClientConfig @@ -328,6 +345,56 @@ func file_proxy_trojan_config_proto_init() { if File_proxy_trojan_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_trojan_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_trojan_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Fallback); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_trojan_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_trojan_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/trojan/config.proto b/proxy/trojan/config.proto index 4229e2ed..6ebed36b 100644 --- a/proxy/trojan/config.proto +++ b/proxy/trojan/config.proto @@ -11,6 +11,7 @@ import "common/protocol/server_spec.proto"; message Account { string password = 1; + string flow = 2; } message Fallback { @@ -28,5 +29,5 @@ message ClientConfig { message ServerConfig { repeated xray.common.protocol.User users = 1; - repeated Fallback fallbacks = 2; + repeated Fallback fallbacks = 3; } diff --git a/proxy/trojan/errors.generated.go b/proxy/trojan/errors.generated.go new file mode 100644 index 00000000..7fb81279 --- /dev/null +++ b/proxy/trojan/errors.generated.go @@ -0,0 +1,9 @@ +package trojan + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/trojan/protocol.go b/proxy/trojan/protocol.go index 96a16638..363cf9e0 100644 --- a/proxy/trojan/protocol.go +++ b/proxy/trojan/protocol.go @@ -5,7 +5,6 @@ import ( "io" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" ) @@ -32,6 +31,7 @@ type ConnWriter struct { io.Writer Target net.Destination Account *MemoryAccount + Flow string headerSent bool } @@ -39,7 +39,7 @@ type ConnWriter struct { func (c *ConnWriter) Write(p []byte) (n int, err error) { if !c.headerSent { if err := c.writeHeader(); err != nil { - return 0, errors.New("failed to write request header").Base(err) + return 0, newError("failed to write request header").Base(err) } } @@ -161,15 +161,15 @@ func (c *ConnReader) ParseHeader() error { var command [1]byte var hash [56]byte if _, err := io.ReadFull(c.Reader, hash[:]); err != nil { - return errors.New("failed to read user hash").Base(err) + return newError("failed to read user hash").Base(err) } if _, err := io.ReadFull(c.Reader, crlf[:]); err != nil { - return errors.New("failed to read crlf").Base(err) + return newError("failed to read crlf").Base(err) } if _, err := io.ReadFull(c.Reader, command[:]); err != nil { - return errors.New("failed to read command").Base(err) + return newError("failed to read command").Base(err) } network := net.Network_TCP @@ -179,12 +179,12 @@ func (c *ConnReader) ParseHeader() error { addr, port, err := addrParser.ReadAddressPort(nil, c.Reader) if err != nil { - return errors.New("failed to read address and port").Base(err) + return newError("failed to read address and port").Base(err) } c.Target = net.Destination{Network: network, Address: addr, Port: port} if _, err := io.ReadFull(c.Reader, crlf[:]); err != nil { - return errors.New("failed to read crlf").Base(err) + return newError("failed to read crlf").Base(err) } c.headerParsed = true @@ -218,22 +218,22 @@ type PacketReader struct { func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { addr, port, err := addrParser.ReadAddressPort(nil, r) if err != nil { - return nil, errors.New("failed to read address and port").Base(err) + return nil, newError("failed to read address and port").Base(err) } var lengthBuf [2]byte if _, err := io.ReadFull(r, lengthBuf[:]); err != nil { - return nil, errors.New("failed to read payload length").Base(err) + return nil, newError("failed to read payload length").Base(err) } remain := int(binary.BigEndian.Uint16(lengthBuf[:])) if remain > maxLength { - return nil, errors.New("oversize payload") + return nil, newError("oversize payload") } var crlf [2]byte if _, err := io.ReadFull(r, crlf[:]); err != nil { - return nil, errors.New("failed to read crlf").Base(err) + return nil, newError("failed to read crlf").Base(err) } dest := net.UDPDestination(addr, port) @@ -250,7 +250,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { n, err := b.ReadFullFrom(r, int32(length)) if err != nil { buf.ReleaseMulti(mb) - return nil, errors.New("failed to read payload").Base(err) + return nil, newError("failed to read payload").Base(err) } remain -= int(n) diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index 44662ac3..029d4eff 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -47,11 +47,11 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { for _, user := range config.Users { u, err := user.ToMemoryUser() if err != nil { - return nil, errors.New("failed to get trojan user").Base(err).AtError() + return nil, newError("failed to get trojan user").Base(err).AtError() } if err := validator.Add(u); err != nil { - return nil, errors.New("failed to add user").Base(err).AtError() + return nil, newError("failed to add user").Base(err).AtError() } } @@ -125,21 +125,6 @@ func (s *Server) RemoveUser(ctx context.Context, e string) error { return s.validator.Del(e) } -// GetUser implements proxy.UserManager.GetUser(). -func (s *Server) GetUser(ctx context.Context, email string) *protocol.MemoryUser { - return s.validator.GetByEmail(email) -} - -// GetUsers implements proxy.UserManager.GetUsers(). -func (s *Server) GetUsers(ctx context.Context) []*protocol.MemoryUser { - return s.validator.GetAll() -} - -// GetUsersCount implements proxy.UserManager.GetUsersCount(). -func (s *Server) GetUsersCount(context.Context) int64 { - return s.validator.GetCount() -} - // Network implements proxy.Inbound.Network(). func (s *Server) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} @@ -147,6 +132,8 @@ func (s *Server) Network() []net.Network { // Process implements proxy.Inbound.Process(). func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { + sid := session.ExportIDToError(ctx) + iConn := conn statConn, ok := iConn.(*stat.CounterConnection) if ok { @@ -155,16 +142,16 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con sessionPolicy := s.policyManager.ForLevel(0) if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { - return errors.New("unable to set read deadline").Base(err).AtWarning() + return newError("unable to set read deadline").Base(err).AtWarning() } first := buf.FromBytes(make([]byte, buf.Size)) first.Clear() firstLen, err := first.ReadFrom(conn) if err != nil { - return errors.New("failed to read first request").Base(err) + return newError("failed to read first request").Base(err) } - errors.LogInfo(ctx, "firstLen = ", firstLen) + newError("firstLen = ", firstLen).AtInfo().WriteToLog(sid) bufferedReader := &buf.BufferedReader{ Reader: buf.NewReader(conn), @@ -179,7 +166,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con shouldFallback := false if firstLen < 58 || first.Byte(56) != '\r' { // invalid protocol - err = errors.New("not trojan protocol") + err = newError("not trojan protocol") log.Record(&log.AccessMessage{ From: conn.RemoteAddr(), To: "", @@ -192,7 +179,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con user = s.validator.Get(hexString(first.BytesTo(56))) if user == nil { // invalid user, let's fallback - err = errors.New("not a valid user") + err = newError("not a valid user") log.Record(&log.AccessMessage{ From: conn.RemoteAddr(), To: "", @@ -205,9 +192,9 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con } if isfb && shouldFallback { - return s.fallback(ctx, err, sessionPolicy, conn, iConn, napfb, first, firstLen, bufferedReader) + return s.fallback(ctx, sid, err, sessionPolicy, conn, iConn, napfb, first, firstLen, bufferedReader) } else if shouldFallback { - return errors.New("invalid protocol or invalid user") + return newError("invalid protocol or invalid user") } clientReader := &ConnReader{Reader: bufferedReader} @@ -218,17 +205,18 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con Status: log.AccessRejected, Reason: err, }) - return errors.New("failed to create request from: ", conn.RemoteAddr()).Base(err) + return newError("failed to create request from: ", conn.RemoteAddr()).Base(err) } destination := clientReader.Target if err := conn.SetReadDeadline(time.Time{}); err != nil { - return errors.New("unable to set read deadline").Base(err).AtWarning() + return newError("unable to set read deadline").Base(err).AtWarning() } inbound := session.InboundFromContext(ctx) - inbound.Name = "trojan" - inbound.CanSpliceCopy = 3 + if inbound == nil { + panic("no inbound metadata") + } inbound.User = user sessionPolicy = s.policyManager.ForLevel(user.Level) @@ -244,8 +232,8 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con Email: user.Email, }) - errors.LogInfo(ctx, "received request for ", destination) - return s.handleConnection(ctx, sessionPolicy, destination, clientReader, buf.NewWriter(conn), dispatcher) + newError("received request for ", destination).WriteToLog(sid) + return s.handleConnection(ctx, sessionPolicy, destination, clientReader, buf.NewWriter(conn), dispatcher, iConn, statConn) } func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error { @@ -256,7 +244,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade } if err := clientWriter.WriteMultiBuffer(buf.MultiBuffer{udpPayload}); err != nil { - errors.LogWarningInner(ctx, err, "failed to write response") + newError("failed to write response").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } }) @@ -273,7 +261,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade mb, err := clientReader.ReadMultiBuffer() if err != nil { if errors.Cause(err) != io.EOF { - return errors.New("unexpected EOF").Base(err) + return newError("unexpected EOF").Base(err) } return nil } @@ -294,7 +282,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade Email: user.Email, }) } - errors.LogInfo(ctx, "tunnelling request to ", destination) + newError("tunnelling request to ", destination).WriteToLog(session.ExportIDToError(ctx)) if !s.cone || dest == nil { dest = &destination @@ -311,7 +299,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Session, destination net.Destination, clientReader buf.Reader, - clientWriter buf.Writer, dispatcher routing.Dispatcher, + clientWriter buf.Writer, dispatcher routing.Dispatcher, iConn stat.Connection, statConn *stat.CounterConnection, ) error { ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) @@ -319,13 +307,13 @@ func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Sess link, err := dispatcher.Dispatch(ctx, destination) if err != nil { - return errors.New("failed to dispatch request to ", destination).Base(err) + return newError("failed to dispatch request to ", destination).Base(err) } requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if buf.Copy(clientReader, link.Writer, buf.UpdateActivity(timer)) != nil { - return errors.New("failed to transfer request").Base(err) + return newError("failed to transfer request").Base(err) } return nil } @@ -334,7 +322,7 @@ func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Sess defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(link.Reader, clientWriter, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to write response").Base(err) + return newError("failed to write response").Base(err) } return nil } @@ -343,17 +331,17 @@ func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Sess if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Must(common.Interrupt(link.Reader)) common.Must(common.Interrupt(link.Writer)) - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil } -func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.Session, connection stat.Connection, iConn stat.Connection, napfb map[string]map[string]map[string]*Fallback, first *buf.Buffer, firstLen int64, reader buf.Reader) error { +func (s *Server) fallback(ctx context.Context, sid errors.ExportOption, err error, sessionPolicy policy.Session, connection stat.Connection, iConn stat.Connection, napfb map[string]map[string]map[string]*Fallback, first *buf.Buffer, firstLen int64, reader buf.Reader) error { if err := connection.SetReadDeadline(time.Time{}); err != nil { - errors.LogWarningInner(ctx, err, "unable to set back read deadline") + newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) } - errors.LogInfoInner(ctx, err, "fallback starts") + newError("fallback starts").Base(err).AtInfo().WriteToLog(sid) name := "" alpn := "" @@ -361,14 +349,14 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S cs := tlsConn.ConnectionState() name = cs.ServerName alpn = cs.NegotiatedProtocol - errors.LogInfo(ctx, "realName = "+name) - errors.LogInfo(ctx, "realAlpn = "+alpn) + newError("realName = " + name).AtInfo().WriteToLog(sid) + newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } else if realityConn, ok := iConn.(*reality.Conn); ok { cs := realityConn.ConnectionState() name = cs.ServerName alpn = cs.NegotiatedProtocol - errors.LogInfo(ctx, "realName = "+name) - errors.LogInfo(ctx, "realAlpn = "+alpn) + newError("realName = " + name).AtInfo().WriteToLog(sid) + newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } name = strings.ToLower(name) alpn = strings.ToLower(alpn) @@ -390,7 +378,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S } apfb := napfb[name] if apfb == nil { - return errors.New(`failed to find the default "name" config`).AtWarning() + return newError(`failed to find the default "name" config`).AtWarning() } if apfb[alpn] == nil { @@ -398,7 +386,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S } pfb := apfb[alpn] if pfb == nil { - return errors.New(`failed to find the default "alpn" config`).AtWarning() + return newError(`failed to find the default "alpn" config`).AtWarning() } path := "" @@ -418,7 +406,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S } if k == '?' || k == ' ' { path = string(firstBytes[i:j]) - errors.LogInfo(ctx, "realPath = "+path) + newError("realPath = " + path).AtInfo().WriteToLog(sid) if pfb[path] == nil { path = "" } @@ -432,7 +420,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S } fb := pfb[path] if fb == nil { - return errors.New(`failed to find the default "path" config`).AtWarning() + return newError(`failed to find the default "path" config`).AtWarning() } ctx, cancel := context.WithCancel(ctx) @@ -448,7 +436,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S } return nil }); err != nil { - return errors.New("failed to dial to " + fb.Dest).Base(err).AtWarning() + return newError("failed to dial to " + fb.Dest).Base(err).AtWarning() } defer conn.Close() @@ -508,11 +496,11 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S common.Must2(pro.Write([]byte{byte(p1 >> 8), byte(p1), byte(p2 >> 8), byte(p2)})) } if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil { - return errors.New("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning() + return newError("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning() } } if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to fallback request payload").Base(err).AtInfo() + return newError("failed to fallback request payload").Base(err).AtInfo() } return nil } @@ -522,7 +510,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to deliver response payload").Base(err).AtInfo() + return newError("failed to deliver response payload").Base(err).AtInfo() } return nil } @@ -530,7 +518,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), task.OnSuccess(getResponse, task.Close(writer))); err != nil { common.Must(common.Interrupt(serverReader)) common.Must(common.Interrupt(serverWriter)) - return errors.New("fallback ends").Base(err).AtInfo() + return newError("fallback ends").Base(err).AtInfo() } return nil diff --git a/proxy/trojan/trojan.go b/proxy/trojan/trojan.go index 73b3154f..4639b7d9 100644 --- a/proxy/trojan/trojan.go +++ b/proxy/trojan/trojan.go @@ -1 +1,5 @@ package trojan + +const ( + muxCoolAddress = "v1.mux.cool" +) diff --git a/proxy/trojan/validator.go b/proxy/trojan/validator.go index 7841a249..26167e4b 100644 --- a/proxy/trojan/validator.go +++ b/proxy/trojan/validator.go @@ -4,7 +4,6 @@ import ( "strings" "sync" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" ) @@ -20,7 +19,7 @@ func (v *Validator) Add(u *protocol.MemoryUser) error { if u.Email != "" { _, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u) if loaded { - return errors.New("User ", u.Email, " already exists.") + return newError("User ", u.Email, " already exists.") } } v.users.Store(hexString(u.Account.(*MemoryAccount).Key), u) @@ -30,12 +29,12 @@ func (v *Validator) Add(u *protocol.MemoryUser) error { // Del a trojan user with a non-empty Email. func (v *Validator) Del(e string) error { if e == "" { - return errors.New("Email must not be empty.") + return newError("Email must not be empty.") } le := strings.ToLower(e) u, _ := v.email.Load(le) if u == nil { - return errors.New("User ", e, " not found.") + return newError("User ", e, " not found.") } v.email.Delete(le) v.users.Delete(hexString(u.(*protocol.MemoryUser).Account.(*MemoryAccount).Key)) @@ -50,33 +49,3 @@ func (v *Validator) Get(hash string) *protocol.MemoryUser { } return nil } - -// Get a trojan user with hashed key, nil if user doesn't exist. -func (v *Validator) GetByEmail(email string) *protocol.MemoryUser { - email = strings.ToLower(email) - u, _ := v.email.Load(email) - if u != nil { - return u.(*protocol.MemoryUser) - } - return nil -} - -// Get all users -func (v *Validator) GetAll() []*protocol.MemoryUser { - var u = make([]*protocol.MemoryUser, 0, 100) - v.email.Range(func(key, value interface{}) bool { - u = append(u, value.(*protocol.MemoryUser)) - return true - }) - return u -} - -// Get users count -func (v *Validator) GetCount() int64 { - var c int64 = 0 - v.email.Range(func(key, value interface{}) bool { - c++ - return true - }) - return c -} diff --git a/proxy/vless/account.go b/proxy/vless/account.go index c22cfe16..b20a9539 100644 --- a/proxy/vless/account.go +++ b/proxy/vless/account.go @@ -1,9 +1,6 @@ package vless import ( - "google.golang.org/protobuf/proto" - - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/uuid" ) @@ -12,7 +9,7 @@ import ( func (a *Account) AsAccount() (protocol.Account, error) { id, err := uuid.ParseString(a.Id) if err != nil { - return nil, errors.New("failed to parse ID").Base(err).AtError() + return nil, newError("failed to parse ID").Base(err).AtError() } return &MemoryAccount{ ID: protocol.NewID(id), @@ -39,11 +36,3 @@ func (a *MemoryAccount) Equals(account protocol.Account) bool { } return a.ID.Equals(vlessAccount.ID) } - -func (a *MemoryAccount) ToProto() proto.Message { - return &Account{ - Id: a.ID.String(), - Flow: a.Flow, - Encryption: a.Encryption, - } -} diff --git a/proxy/vless/account.pb.go b/proxy/vless/account.pb.go index fd5d4518..a52fc8f1 100644 --- a/proxy/vless/account.pb.go +++ b/proxy/vless/account.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/vless/account.proto package vless @@ -35,9 +35,11 @@ type Account struct { func (x *Account) Reset() { *x = Account{} - mi := &file_proxy_vless_account_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vless_account_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Account) String() string { @@ -48,7 +50,7 @@ func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_account_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -116,7 +118,7 @@ func file_proxy_vless_account_proto_rawDescGZIP() []byte { } var file_proxy_vless_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_vless_account_proto_goTypes = []any{ +var file_proxy_vless_account_proto_goTypes = []interface{}{ (*Account)(nil), // 0: xray.proxy.vless.Account } var file_proxy_vless_account_proto_depIdxs = []int32{ @@ -132,6 +134,20 @@ func file_proxy_vless_account_proto_init() { if File_proxy_vless_account_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_vless_account_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/vless/encoding/addons.go b/proxy/vless/encoding/addons.go index 4474e3c9..d62e400f 100644 --- a/proxy/vless/encoding/addons.go +++ b/proxy/vless/encoding/addons.go @@ -1,15 +1,12 @@ package encoding import ( - "context" "io" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/proxy/vless" - "google.golang.org/protobuf/proto" ) func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error { @@ -17,17 +14,17 @@ func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error { case vless.XRV: bytes, err := proto.Marshal(addons) if err != nil { - return errors.New("failed to marshal addons protobuf value").Base(err) + return newError("failed to marshal addons protobuf value").Base(err) } if err := buffer.WriteByte(byte(len(bytes))); err != nil { - return errors.New("failed to write addons protobuf length").Base(err) + return newError("failed to write addons protobuf length").Base(err) } if _, err := buffer.Write(bytes); err != nil { - return errors.New("failed to write addons protobuf value").Base(err) + return newError("failed to write addons protobuf value").Base(err) } default: if err := buffer.WriteByte(0); err != nil { - return errors.New("failed to write addons protobuf length").Base(err) + return newError("failed to write addons protobuf length").Base(err) } } @@ -38,17 +35,17 @@ func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) { addons := new(Addons) buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { - return nil, errors.New("failed to read addons protobuf length").Base(err) + return nil, newError("failed to read addons protobuf length").Base(err) } if length := int32(buffer.Byte(0)); length != 0 { buffer.Clear() if _, err := buffer.ReadFullFrom(reader, length); err != nil { - return nil, errors.New("failed to read addons protobuf value").Base(err) + return nil, newError("failed to read addons protobuf value").Base(err) } if err := proto.Unmarshal(buffer.Bytes(), addons); err != nil { - return nil, errors.New("failed to unmarshal addons protobuf value").Base(err) + return nil, newError("failed to unmarshal addons protobuf value").Base(err) } // Verification. @@ -61,15 +58,14 @@ func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) { } // EncodeBodyAddons returns a Writer that auto-encrypt content written by caller. -func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *proxy.TrafficState, isUplink bool, context context.Context) buf.Writer { - if request.Command == protocol.RequestCommandUDP { - return NewMultiLengthPacketWriter(writer.(buf.Writer)) +func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, addons *Addons) buf.Writer { + switch addons.Flow { + default: + if request.Command == protocol.RequestCommandUDP { + return NewMultiLengthPacketWriter(writer.(buf.Writer)) + } } - w := buf.NewWriter(writer) - if requestAddons.Flow == vless.XRV { - w = proxy.NewVisionWriter(w, state, isUplink, context) - } - return w + return buf.NewWriter(writer) } // DecodeBodyAddons returns a Reader from which caller can fetch decrypted body. @@ -150,7 +146,7 @@ func (w *LengthPacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { mb[i] = nil } if _, err := w.Write(w.cache); err != nil { - return errors.New("failed to write a packet").Base(err) + return newError("failed to write a packet").Base(err) } return nil } @@ -169,7 +165,7 @@ type LengthPacketReader struct { func (r *LengthPacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if _, err := io.ReadFull(r.Reader, r.cache); err != nil { // maybe EOF - return nil, errors.New("failed to read packet length").Base(err) + return nil, newError("failed to read packet length").Base(err) } length := int32(r.cache[0])<<8 | int32(r.cache[1]) // fmt.Println("Read", length) @@ -182,7 +178,7 @@ func (r *LengthPacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { length -= size b := buf.New() if _, err := b.ReadFullFrom(r.Reader, size); err != nil { - return nil, errors.New("failed to read packet payload").Base(err) + return nil, newError("failed to read packet payload").Base(err) } mb = append(mb, b) } diff --git a/proxy/vless/encoding/addons.pb.go b/proxy/vless/encoding/addons.pb.go index 2f518841..a5b97f81 100644 --- a/proxy/vless/encoding/addons.pb.go +++ b/proxy/vless/encoding/addons.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/vless/encoding/addons.proto package encoding @@ -31,9 +31,11 @@ type Addons struct { func (x *Addons) Reset() { *x = Addons{} - mi := &file_proxy_vless_encoding_addons_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vless_encoding_addons_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Addons) String() string { @@ -44,7 +46,7 @@ func (*Addons) ProtoMessage() {} func (x *Addons) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_encoding_addons_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -106,7 +108,7 @@ func file_proxy_vless_encoding_addons_proto_rawDescGZIP() []byte { } var file_proxy_vless_encoding_addons_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_vless_encoding_addons_proto_goTypes = []any{ +var file_proxy_vless_encoding_addons_proto_goTypes = []interface{}{ (*Addons)(nil), // 0: xray.proxy.vless.encoding.Addons } var file_proxy_vless_encoding_addons_proto_depIdxs = []int32{ @@ -122,6 +124,20 @@ func file_proxy_vless_encoding_addons_proto_init() { if File_proxy_vless_encoding_addons_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_vless_encoding_addons_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Addons); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/vless/encoding/encoding.go b/proxy/vless/encoding/encoding.go index 38043e68..b96acee9 100644 --- a/proxy/vless/encoding/encoding.go +++ b/proxy/vless/encoding/encoding.go @@ -1,9 +1,17 @@ package encoding +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "bytes" "context" + "crypto/rand" "io" + "math/big" + "runtime" + "strconv" + "syscall" + "time" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" @@ -12,14 +20,39 @@ import ( "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/features/stats" - "github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" ) const ( Version = byte(0) ) +var ( + tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04} + tlsClientHandShakeStart = []byte{0x16, 0x03} + tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03} + tlsApplicationDataStart = []byte{0x17, 0x03, 0x03} + + Tls13CipherSuiteDic = map[uint16]string{ + 0x1301: "TLS_AES_128_GCM_SHA256", + 0x1302: "TLS_AES_256_GCM_SHA384", + 0x1303: "TLS_CHACHA20_POLY1305_SHA256", + 0x1304: "TLS_AES_128_CCM_SHA256", + 0x1305: "TLS_AES_128_CCM_8_SHA256", + } +) + +const ( + tlsHandshakeTypeClientHello byte = 0x01 + tlsHandshakeTypeServerHello byte = 0x02 + + CommandPaddingContinue byte = 0x00 + CommandPaddingEnd byte = 0x01 + CommandPaddingDirect byte = 0x02 +) + var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), @@ -33,36 +66,36 @@ func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requ defer buffer.Release() if err := buffer.WriteByte(request.Version); err != nil { - return errors.New("failed to write request version").Base(err) + return newError("failed to write request version").Base(err) } if _, err := buffer.Write(request.User.Account.(*vless.MemoryAccount).ID.Bytes()); err != nil { - return errors.New("failed to write request user id").Base(err) + return newError("failed to write request user id").Base(err) } if err := EncodeHeaderAddons(&buffer, requestAddons); err != nil { - return errors.New("failed to encode request header addons").Base(err) + return newError("failed to encode request header addons").Base(err) } if err := buffer.WriteByte(byte(request.Command)); err != nil { - return errors.New("failed to write request command").Base(err) + return newError("failed to write request command").Base(err) } if request.Command != protocol.RequestCommandMux { if err := addrParser.WriteAddressPort(&buffer, request.Address, request.Port); err != nil { - return errors.New("failed to write request address and port").Base(err) + return newError("failed to write request address and port").Base(err) } } if _, err := writer.Write(buffer.Bytes()); err != nil { - return errors.New("failed to write request header").Base(err) + return newError("failed to write request header").Base(err) } return nil } // DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. -func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator vless.Validator) (*protocol.RequestHeader, *Addons, bool, error) { +func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator *vless.Validator) (*protocol.RequestHeader, *Addons, bool, error) { buffer := buf.StackNew() defer buffer.Release() @@ -72,7 +105,7 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat request.Version = first.Byte(0) } else { if _, err := buffer.ReadFullFrom(reader, 1); err != nil { - return nil, nil, false, errors.New("failed to read request version").Base(err) + return nil, nil, false, newError("failed to read request version").Base(err) } request.Version = buffer.Byte(0) } @@ -87,13 +120,13 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat } else { buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 16); err != nil { - return nil, nil, false, errors.New("failed to read request user id").Base(err) + return nil, nil, false, newError("failed to read request user id").Base(err) } copy(id[:], buffer.Bytes()) } if request.User = validator.Get(id); request.User == nil { - return nil, nil, isfb, errors.New("invalid request user id") + return nil, nil, isfb, newError("invalid request user id") } if isfb { @@ -102,12 +135,12 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat requestAddons, err := DecodeHeaderAddons(&buffer, reader) if err != nil { - return nil, nil, false, errors.New("failed to decode request header addons").Base(err) + return nil, nil, false, newError("failed to decode request header addons").Base(err) } buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { - return nil, nil, false, errors.New("failed to read request command").Base(err) + return nil, nil, false, newError("failed to read request command").Base(err) } request.Command = protocol.RequestCommand(buffer.Byte(0)) @@ -122,11 +155,11 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat } } if request.Address == nil { - return nil, nil, false, errors.New("invalid request address") + return nil, nil, false, newError("invalid request address") } return request, requestAddons, false, nil default: - return nil, nil, isfb, errors.New("invalid request version") + return nil, nil, isfb, newError("invalid request version") } } @@ -136,15 +169,15 @@ func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, res defer buffer.Release() if err := buffer.WriteByte(request.Version); err != nil { - return errors.New("failed to write response version").Base(err) + return newError("failed to write response version").Base(err) } if err := EncodeHeaderAddons(&buffer, responseAddons); err != nil { - return errors.New("failed to encode response header addons").Base(err) + return newError("failed to encode response header addons").Base(err) } if _, err := writer.Write(buffer.Bytes()); err != nil { - return errors.New("failed to write response header").Base(err) + return newError("failed to write response header").Base(err) } return nil @@ -156,96 +189,102 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A defer buffer.Release() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { - return nil, errors.New("failed to read response version").Base(err) + return nil, newError("failed to read response version").Base(err) } if buffer.Byte(0) != request.Version { - return nil, errors.New("unexpected response version. Expecting ", int(request.Version), " but actually ", int(buffer.Byte(0))) + return nil, newError("unexpected response version. Expecting ", int(request.Version), " but actually ", int(buffer.Byte(0))) } responseAddons, err := DecodeHeaderAddons(&buffer, reader) if err != nil { - return nil, errors.New("failed to decode response header addons").Base(err) + return nil, newError("failed to decode response header addons").Base(err) } return responseAddons, nil } // XtlsRead filter and read xtls protocol -func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error { - err := func() error { - for { - if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy { - var writerConn net.Conn - var inTimer *signal.ActivityTimer - if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil { - writerConn = inbound.Conn - inTimer = inbound.Timer - if isUplink && inbound.CanSpliceCopy == 2 { - inbound.CanSpliceCopy = 1 - } - if !isUplink && ob != nil && ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can change - ob.CanSpliceCopy = 1 - } - } - return proxy.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer, inTimer) - } - buffer, err := reader.ReadMultiBuffer() - if !buffer.IsEmpty() { - timer.Update() - if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy { - // XTLS Vision processes struct TLS Conn's input and rawInput - if inputBuffer, err := buf.ReadFrom(input); err == nil { - if !inputBuffer.IsEmpty() { - buffer, _ = buf.MergeMulti(buffer, inputBuffer) - } - } - if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil { - if !rawInputBuffer.IsEmpty() { - buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) - } - } - } - if werr := writer.WriteMultiBuffer(buffer); werr != nil { - return werr - } - } - if err != nil { - return err - } - } - }() - if err != nil && errors.Cause(err) != io.EOF { - return err - } - return nil -} - -// XtlsWrite filter and write xtls protocol -func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error { +func XtlsRead(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, rawConn syscall.RawConn, + input *bytes.Reader, rawInput *bytes.Buffer, + counter stats.Counter, ctx context.Context, userUUID []byte, numberOfPacketToFilter *int, enableXtls *bool, + isTLS12orAbove *bool, isTLS *bool, cipher *uint16, remainingServerHello *int32, +) error { err := func() error { var ct stats.Counter + withinPaddingBuffers := true + shouldSwitchToDirectCopy := false + var remainingContent int32 = -1 + var remainingPadding int32 = -1 + currentCommand := 0 for { - buffer, err := reader.ReadMultiBuffer() - if isUplink && trafficState.Outbound.UplinkWriterDirectCopy || !isUplink && trafficState.Inbound.DownlinkWriterDirectCopy { - if inbound := session.InboundFromContext(ctx); inbound != nil { - if !isUplink && inbound.CanSpliceCopy == 2 { - inbound.CanSpliceCopy = 1 - } - if isUplink && ob != nil && ob.CanSpliceCopy == 2 { - ob.CanSpliceCopy = 1 + if shouldSwitchToDirectCopy { + shouldSwitchToDirectCopy = false + if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil && (runtime.GOOS == "linux" || runtime.GOOS == "android") { + if _, ok := inbound.User.Account.(*vless.MemoryAccount); inbound.User.Account == nil || ok { + iConn := inbound.Conn + statConn, ok := iConn.(*stat.CounterConnection) + if ok { + iConn = statConn.Connection + } + if xc, ok := iConn.(*tls.Conn); ok { + iConn = xc.NetConn() + } + if tc, ok := iConn.(*net.TCPConn); ok { + newError("XtlsRead splice").WriteToLog(session.ExportIDToError(ctx)) + runtime.Gosched() // necessary + w, err := tc.ReadFrom(conn) + if counter != nil { + counter.Add(w) + } + if statConn != nil && statConn.WriteCounter != nil { + statConn.WriteCounter.Add(w) + } + return err + } } } - rawConn, _, writerCounter := proxy.UnwrapRawConn(conn) - writer = buf.NewWriter(rawConn) - ct = writerCounter - if isUplink { - trafficState.Outbound.UplinkWriterDirectCopy = false - } else { - trafficState.Inbound.DownlinkWriterDirectCopy = false - } + reader = buf.NewReadVReader(conn, rawConn, nil) + ct = counter + newError("XtlsRead readV").WriteToLog(session.ExportIDToError(ctx)) } + buffer, err := reader.ReadMultiBuffer() if !buffer.IsEmpty() { + if withinPaddingBuffers || *numberOfPacketToFilter > 0 { + buffer = XtlsUnpadding(ctx, buffer, userUUID, &remainingContent, &remainingPadding, ¤tCommand) + if remainingContent == 0 && remainingPadding == 0 { + if currentCommand == 1 { + withinPaddingBuffers = false + remainingContent = -1 + remainingPadding = -1 // set to initial state to parse the next padding + } else if currentCommand == 2 { + withinPaddingBuffers = false + shouldSwitchToDirectCopy = true + // XTLS Vision processes struct TLS Conn's input and rawInput + if inputBuffer, err := buf.ReadFrom(input); err == nil { + if !inputBuffer.IsEmpty() { + buffer, _ = buf.MergeMulti(buffer, inputBuffer) + } + } + if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil { + if !rawInputBuffer.IsEmpty() { + buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) + } + } + } else if currentCommand == 0 { + withinPaddingBuffers = true + } else { + newError("XtlsRead unknown command ", currentCommand, buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } + } else if remainingContent > 0 || remainingPadding > 0 { + withinPaddingBuffers = true + } else { + withinPaddingBuffers = false + } + } + if *numberOfPacketToFilter > 0 { + XtlsFilterTls(buffer, numberOfPacketToFilter, enableXtls, isTLS12orAbove, isTLS, cipher, remainingServerHello, ctx) + } if ct != nil { ct.Add(int64(buffer.Len())) } @@ -264,3 +303,274 @@ func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdate } return nil } + +// XtlsWrite filter and write xtls protocol +func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, counter stats.Counter, + ctx context.Context, numberOfPacketToFilter *int, enableXtls *bool, isTLS12orAbove *bool, isTLS *bool, + cipher *uint16, remainingServerHello *int32, +) error { + err := func() error { + var ct stats.Counter + isPadding := true + shouldSwitchToDirectCopy := false + for { + buffer, err := reader.ReadMultiBuffer() + if !buffer.IsEmpty() { + if *numberOfPacketToFilter > 0 { + XtlsFilterTls(buffer, numberOfPacketToFilter, enableXtls, isTLS12orAbove, isTLS, cipher, remainingServerHello, ctx) + } + if isPadding { + buffer = ReshapeMultiBuffer(ctx, buffer) + var xtlsSpecIndex int + for i, b := range buffer { + if *isTLS && b.Len() >= 6 && bytes.Equal(tlsApplicationDataStart, b.BytesTo(3)) { + var command byte = CommandPaddingEnd + if *enableXtls { + shouldSwitchToDirectCopy = true + xtlsSpecIndex = i + command = CommandPaddingDirect + } + isPadding = false + buffer[i] = XtlsPadding(b, command, nil, *isTLS, ctx) + break + } else if !*isTLS12orAbove && *numberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early + isPadding = false + buffer[i] = XtlsPadding(b, CommandPaddingEnd, nil, *isTLS, ctx) + break + } + buffer[i] = XtlsPadding(b, CommandPaddingContinue, nil, *isTLS, ctx) + } + if shouldSwitchToDirectCopy { + encryptBuffer, directBuffer := buf.SplitMulti(buffer, xtlsSpecIndex+1) + length := encryptBuffer.Len() + if !encryptBuffer.IsEmpty() { + timer.Update() + if werr := writer.WriteMultiBuffer(encryptBuffer); werr != nil { + return werr + } + } + buffer = directBuffer + writer = buf.NewWriter(conn) + ct = counter + newError("XtlsWrite writeV ", xtlsSpecIndex, " ", length, " ", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + time.Sleep(5 * time.Millisecond) // for some device, the first xtls direct packet fails without this delay + } + } + if !buffer.IsEmpty() { + if ct != nil { + ct.Add(int64(buffer.Len())) + } + timer.Update() + if werr := writer.WriteMultiBuffer(buffer); werr != nil { + return werr + } + } + } + if err != nil { + return err + } + } + }() + if err != nil && errors.Cause(err) != io.EOF { + return err + } + return nil +} + +// XtlsFilterTls filter and recognize tls 1.3 and other info +func XtlsFilterTls(buffer buf.MultiBuffer, numberOfPacketToFilter *int, enableXtls *bool, isTLS12orAbove *bool, isTLS *bool, + cipher *uint16, remainingServerHello *int32, ctx context.Context, +) { + for _, b := range buffer { + *numberOfPacketToFilter-- + if b.Len() >= 6 { + startsBytes := b.BytesTo(6) + if bytes.Equal(tlsServerHandShakeStart, startsBytes[:3]) && startsBytes[5] == tlsHandshakeTypeServerHello { + *remainingServerHello = (int32(startsBytes[3])<<8 | int32(startsBytes[4])) + 5 + *isTLS12orAbove = true + *isTLS = true + if b.Len() >= 79 && *remainingServerHello >= 79 { + sessionIdLen := int32(b.Byte(43)) + cipherSuite := b.BytesRange(43+sessionIdLen+1, 43+sessionIdLen+3) + *cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1]) + } else { + newError("XtlsFilterTls short server hello, tls 1.2 or older? ", b.Len(), " ", *remainingServerHello).WriteToLog(session.ExportIDToError(ctx)) + } + } else if bytes.Equal(tlsClientHandShakeStart, startsBytes[:2]) && startsBytes[5] == tlsHandshakeTypeClientHello { + *isTLS = true + newError("XtlsFilterTls found tls client hello! ", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } + } + if *remainingServerHello > 0 { + end := *remainingServerHello + if end > b.Len() { + end = b.Len() + } + *remainingServerHello -= b.Len() + if bytes.Contains(b.BytesTo(end), tls13SupportedVersions) { + v, ok := Tls13CipherSuiteDic[*cipher] + if !ok { + v = "Old cipher: " + strconv.FormatUint(uint64(*cipher), 16) + } else if v != "TLS_AES_128_CCM_8_SHA256" { + *enableXtls = true + } + newError("XtlsFilterTls found tls 1.3! ", b.Len(), " ", v).WriteToLog(session.ExportIDToError(ctx)) + *numberOfPacketToFilter = 0 + return + } else if *remainingServerHello <= 0 { + newError("XtlsFilterTls found tls 1.2! ", b.Len()).WriteToLog(session.ExportIDToError(ctx)) + *numberOfPacketToFilter = 0 + return + } + newError("XtlsFilterTls inconclusive server hello ", b.Len(), " ", *remainingServerHello).WriteToLog(session.ExportIDToError(ctx)) + } + if *numberOfPacketToFilter <= 0 { + newError("XtlsFilterTls stop filtering", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } + } +} + +// ReshapeMultiBuffer prepare multi buffer for padding stucture (max 21 bytes) +func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBuffer { + needReshape := 0 + for _, b := range buffer { + if b.Len() >= buf.Size-21 { + needReshape += 1 + } + } + if needReshape == 0 { + return buffer + } + mb2 := make(buf.MultiBuffer, 0, len(buffer)+needReshape) + toPrint := "" + for i, buffer1 := range buffer { + if buffer1.Len() >= buf.Size-21 { + index := int32(bytes.LastIndex(buffer1.Bytes(), tlsApplicationDataStart)) + if index <= 0 || index > buf.Size-21 { + index = buf.Size / 2 + } + buffer2 := buf.New() + buffer2.Write(buffer1.BytesFrom(index)) + buffer1.Resize(0, index) + mb2 = append(mb2, buffer1, buffer2) + toPrint += " " + strconv.Itoa(int(buffer1.Len())) + " " + strconv.Itoa(int(buffer2.Len())) + } else { + mb2 = append(mb2, buffer1) + toPrint += " " + strconv.Itoa(int(buffer1.Len())) + } + buffer[i] = nil + } + buffer = buffer[:0] + newError("ReshapeMultiBuffer ", toPrint).WriteToLog(session.ExportIDToError(ctx)) + return mb2 +} + +// XtlsPadding add padding to eliminate length siganature during tls handshake +func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer { + var contentLen int32 = 0 + var paddingLen int32 = 0 + if b != nil { + contentLen = b.Len() + } + if contentLen < 900 && longPadding { + l, err := rand.Int(rand.Reader, big.NewInt(500)) + if err != nil { + newError("failed to generate padding").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + paddingLen = int32(l.Int64()) + 900 - contentLen + } else { + l, err := rand.Int(rand.Reader, big.NewInt(256)) + if err != nil { + newError("failed to generate padding").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + paddingLen = int32(l.Int64()) + } + if paddingLen > buf.Size - 21 - contentLen { + paddingLen = buf.Size - 21 - contentLen + } + newbuffer := buf.New() + if userUUID != nil { + newbuffer.Write(*userUUID) + *userUUID = nil + } + newbuffer.Write([]byte{command, byte(contentLen >> 8), byte(contentLen), byte(paddingLen >> 8), byte(paddingLen)}) + if b != nil { + newbuffer.Write(b.Bytes()) + b.Release() + b = nil + } + newbuffer.Extend(paddingLen) + newError("XtlsPadding ", contentLen, " ", paddingLen, " ", command).WriteToLog(session.ExportIDToError(ctx)) + return newbuffer +} + +// XtlsUnpadding remove padding and parse command +func XtlsUnpadding(ctx context.Context, buffer buf.MultiBuffer, userUUID []byte, remainingContent *int32, remainingPadding *int32, currentCommand *int) buf.MultiBuffer { + posindex := 0 + var posByte int32 = 0 + if *remainingContent == -1 && *remainingPadding == -1 { + for i, b := range buffer { + if b.Len() >= 21 && bytes.Equal(userUUID, b.BytesTo(16)) { + posindex = i + posByte = 16 + *remainingContent = 0 + *remainingPadding = 0 + *currentCommand = 0 + break + } + } + } + if *remainingContent == -1 && *remainingPadding == -1 { + return buffer + } + mb2 := make(buf.MultiBuffer, 0, len(buffer)) + for i := 0; i < posindex; i++ { + newbuffer := buf.New() + newbuffer.Write(buffer[i].Bytes()) + mb2 = append(mb2, newbuffer) + } + for i := posindex; i < len(buffer); i++ { + b := buffer[i] + for posByte < b.Len() { + if *remainingContent <= 0 && *remainingPadding <= 0 { + if *currentCommand == 1 { // possible buffer after padding, no need to worry about xtls (command 2) + len := b.Len() - posByte + newbuffer := buf.New() + newbuffer.Write(b.BytesRange(posByte, posByte+len)) + mb2 = append(mb2, newbuffer) + posByte += len + } else { + paddingInfo := b.BytesRange(posByte, posByte+5) + *currentCommand = int(paddingInfo[0]) + *remainingContent = int32(paddingInfo[1])<<8 | int32(paddingInfo[2]) + *remainingPadding = int32(paddingInfo[3])<<8 | int32(paddingInfo[4]) + newError("Xtls Unpadding new block", i, " ", posByte, " content ", *remainingContent, " padding ", *remainingPadding, " ", paddingInfo[0]).WriteToLog(session.ExportIDToError(ctx)) + posByte += 5 + } + } else if *remainingContent > 0 { + len := *remainingContent + if b.Len() < posByte+*remainingContent { + len = b.Len() - posByte + } + newbuffer := buf.New() + newbuffer.Write(b.BytesRange(posByte, posByte+len)) + mb2 = append(mb2, newbuffer) + *remainingContent -= len + posByte += len + } else { // remainingPadding > 0 + len := *remainingPadding + if b.Len() < posByte+*remainingPadding { + len = b.Len() - posByte + } + *remainingPadding -= len + posByte += len + } + if posByte == b.Len() { + posByte = 0 + break + } + } + } + buf.ReleaseMulti(buffer) + return mb2 +} diff --git a/proxy/vless/encoding/encoding_test.go b/proxy/vless/encoding/encoding_test.go index 9180154a..ee7c6df0 100644 --- a/proxy/vless/encoding/encoding_test.go +++ b/proxy/vless/encoding/encoding_test.go @@ -42,7 +42,7 @@ func TestRequestSerialization(t *testing.T) { buffer := buf.StackNew() common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) - Validator := new(vless.MemoryValidator) + Validator := new(vless.Validator) Validator.Add(user) actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) @@ -83,7 +83,7 @@ func TestInvalidRequest(t *testing.T) { buffer := buf.StackNew() common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) - Validator := new(vless.MemoryValidator) + Validator := new(vless.Validator) Validator.Add(user) _, _, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) @@ -114,7 +114,7 @@ func TestMuxRequest(t *testing.T) { buffer := buf.StackNew() common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) - Validator := new(vless.MemoryValidator) + Validator := new(vless.Validator) Validator.Add(user) actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) diff --git a/proxy/vless/encoding/errors.generated.go b/proxy/vless/encoding/errors.generated.go new file mode 100644 index 00000000..267711d9 --- /dev/null +++ b/proxy/vless/encoding/errors.generated.go @@ -0,0 +1,9 @@ +package encoding + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/errors.generated.go b/proxy/vless/errors.generated.go new file mode 100644 index 00000000..9487ff0e --- /dev/null +++ b/proxy/vless/errors.generated.go @@ -0,0 +1,9 @@ +package vless + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/inbound/config.pb.go b/proxy/vless/inbound/config.pb.go index 907a3f7f..4061e120 100644 --- a/proxy/vless/inbound/config.pb.go +++ b/proxy/vless/inbound/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/vless/inbound/config.proto package inbound @@ -36,9 +36,11 @@ type Fallback struct { func (x *Fallback) Reset() { *x = Fallback{} - mi := &file_proxy_vless_inbound_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vless_inbound_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Fallback) String() string { @@ -49,7 +51,7 @@ func (*Fallback) ProtoMessage() {} func (x *Fallback) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_inbound_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -120,9 +122,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_vless_inbound_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vless_inbound_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -133,7 +137,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_inbound_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -219,7 +223,7 @@ func file_proxy_vless_inbound_config_proto_rawDescGZIP() []byte { } var file_proxy_vless_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_proxy_vless_inbound_config_proto_goTypes = []any{ +var file_proxy_vless_inbound_config_proto_goTypes = []interface{}{ (*Fallback)(nil), // 0: xray.proxy.vless.inbound.Fallback (*Config)(nil), // 1: xray.proxy.vless.inbound.Config (*protocol.User)(nil), // 2: xray.common.protocol.User @@ -239,6 +243,32 @@ func file_proxy_vless_inbound_config_proto_init() { if File_proxy_vless_inbound_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_vless_inbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Fallback); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_vless_inbound_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/vless/inbound/errors.generated.go b/proxy/vless/inbound/errors.generated.go new file mode 100644 index 00000000..c2d7295e --- /dev/null +++ b/proxy/vless/inbound/errors.generated.go @@ -0,0 +1,9 @@ +package inbound + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 1da2e091..b3def4bb 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -1,5 +1,7 @@ package inbound +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "bytes" "context" @@ -8,9 +10,11 @@ import ( "reflect" "strconv" "strings" + "syscall" "time" "unsafe" + "github.com/pires/go-proxyproto" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" @@ -26,7 +30,7 @@ import ( feature_inbound "github.com/xtls/xray-core/features/inbound" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/proxy" + "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless/encoding" "github.com/xtls/xray-core/transport/internet/reality" @@ -43,21 +47,7 @@ func init() { }); err != nil { return nil, err } - - c := config.(*Config) - - validator := new(vless.MemoryValidator) - for _, user := range c.Clients { - u, err := user.ToMemoryUser() - if err != nil { - return nil, errors.New("failed to get VLESS user").Base(err).AtError() - } - if err := validator.Add(u); err != nil { - return nil, errors.New("failed to initiate user").Base(err).AtError() - } - } - - return New(ctx, c, dc, validator) + return New(ctx, config.(*Config), dc) })) } @@ -65,20 +55,30 @@ func init() { type Handler struct { inboundHandlerManager feature_inbound.Manager policyManager policy.Manager - validator vless.Validator + validator *vless.Validator dns dns.Client fallbacks map[string]map[string]map[string]*Fallback // or nil // regexps map[string]*regexp.Regexp // or nil } // New creates a new VLess inbound handler. -func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Validator) (*Handler, error) { +func New(ctx context.Context, config *Config, dc dns.Client) (*Handler, error) { v := core.MustFromContext(ctx) handler := &Handler{ inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + validator: new(vless.Validator), dns: dc, - validator: validator, + } + + for _, user := range config.Clients { + u, err := user.ToMemoryUser() + if err != nil { + return nil, newError("failed to get VLESS user").Base(err).AtError() + } + if err := handler.AddUser(ctx, u); err != nil { + return nil, newError("failed to initiate user").Base(err).AtError() + } } if config.Fallbacks != nil { @@ -95,7 +95,7 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val /* if fb.Path != "" { if r, err := regexp.Compile(fb.Path); err != nil { - return nil, errors.New("invalid path regexp").Base(err).AtError() + return nil, newError("invalid path regexp").Base(err).AtError() } else { handler.regexps[fb.Path] = r } @@ -172,21 +172,6 @@ func (h *Handler) RemoveUser(ctx context.Context, e string) error { return h.validator.Del(e) } -// GetUser implements proxy.UserManager.GetUser(). -func (h *Handler) GetUser(ctx context.Context, email string) *protocol.MemoryUser { - return h.validator.GetByEmail(email) -} - -// GetUsers implements proxy.UserManager.GetUsers(). -func (h *Handler) GetUsers(ctx context.Context) []*protocol.MemoryUser { - return h.validator.GetAll() -} - -// GetUsersCount implements proxy.UserManager.GetUsersCount(). -func (h *Handler) GetUsersCount(context.Context) int64 { - return h.validator.GetCount() -} - // Network implements proxy.Inbound.Network(). func (*Handler) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} @@ -194,20 +179,23 @@ func (*Handler) Network() []net.Network { // Process implements proxy.Inbound.Process(). func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { + sid := session.ExportIDToError(ctx) + iConn := connection - if statConn, ok := iConn.(*stat.CounterConnection); ok { + statConn, ok := iConn.(*stat.CounterConnection) + if ok { iConn = statConn.Connection } sessionPolicy := h.policyManager.ForLevel(0) if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { - return errors.New("unable to set read deadline").Base(err).AtWarning() + return newError("unable to set read deadline").Base(err).AtWarning() } first := buf.FromBytes(make([]byte, buf.Size)) first.Clear() firstLen, _ := first.ReadFrom(connection) - errors.LogInfo(ctx, "firstLen = ", firstLen) + newError("firstLen = ", firstLen).AtInfo().WriteToLog(sid) reader := &buf.BufferedReader{ Reader: buf.NewReader(connection), @@ -222,7 +210,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s isfb := napfb != nil if isfb && firstLen < 18 { - err = errors.New("fallback directly") + err = newError("fallback directly") } else { request, requestAddons, isfb, err = encoding.DecodeRequestHeader(isfb, first, reader, h.validator) } @@ -230,9 +218,9 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if err != nil { if isfb { if err := connection.SetReadDeadline(time.Time{}); err != nil { - errors.LogWarningInner(ctx, err, "unable to set back read deadline") + newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) } - errors.LogInfoInner(ctx, err, "fallback starts") + newError("fallback starts").Base(err).AtInfo().WriteToLog(sid) name := "" alpn := "" @@ -240,14 +228,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s cs := tlsConn.ConnectionState() name = cs.ServerName alpn = cs.NegotiatedProtocol - errors.LogInfo(ctx, "realName = "+name) - errors.LogInfo(ctx, "realAlpn = "+alpn) + newError("realName = " + name).AtInfo().WriteToLog(sid) + newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } else if realityConn, ok := iConn.(*reality.Conn); ok { cs := realityConn.ConnectionState() name = cs.ServerName alpn = cs.NegotiatedProtocol - errors.LogInfo(ctx, "realName = "+name) - errors.LogInfo(ctx, "realAlpn = "+alpn) + newError("realName = " + name).AtInfo().WriteToLog(sid) + newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } name = strings.ToLower(name) alpn = strings.ToLower(alpn) @@ -269,7 +257,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } apfb := napfb[name] if apfb == nil { - return errors.New(`failed to find the default "name" config`).AtWarning() + return newError(`failed to find the default "name" config`).AtWarning() } if apfb[alpn] == nil { @@ -277,7 +265,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } pfb := apfb[alpn] if pfb == nil { - return errors.New(`failed to find the default "alpn" config`).AtWarning() + return newError(`failed to find the default "alpn" config`).AtWarning() } path := "" @@ -286,7 +274,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if lines := bytes.Split(firstBytes, []byte{'\r', '\n'}); len(lines) > 1 { if s := bytes.Split(lines[0], []byte{' '}); len(s) == 3 { if len(s[0]) < 8 && len(s[1]) > 0 && len(s[2]) == 8 { - errors.New("realPath = " + string(s[1])).AtInfo().WriteToLog(sid) + newError("realPath = " + string(s[1])).AtInfo().WriteToLog(sid) for _, fb := range pfb { if fb.Path != "" && h.regexps[fb.Path].Match(s[1]) { path = fb.Path @@ -312,7 +300,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } if k == '?' || k == ' ' { path = string(firstBytes[i:j]) - errors.LogInfo(ctx, "realPath = "+path) + newError("realPath = " + path).AtInfo().WriteToLog(sid) if pfb[path] == nil { path = "" } @@ -326,7 +314,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } fb := pfb[path] if fb == nil { - return errors.New(`failed to find the default "path" config`).AtWarning() + return newError(`failed to find the default "path" config`).AtWarning() } ctx, cancel := context.WithCancel(ctx) @@ -342,7 +330,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } return nil }); err != nil { - return errors.New("failed to dial to " + fb.Dest).Base(err).AtWarning() + return newError("failed to dial to " + fb.Dest).Base(err).AtWarning() } defer conn.Close() @@ -402,11 +390,11 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s pro.Write([]byte{byte(p1 >> 8), byte(p1), byte(p2 >> 8), byte(p2)}) } if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil { - return errors.New("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning() + return newError("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning() } } if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to fallback request payload").Base(err).AtInfo() + return newError("failed to fallback request payload").Base(err).AtInfo() } return nil } @@ -416,7 +404,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to deliver response payload").Base(err).AtInfo() + return newError("failed to deliver response payload").Base(err).AtInfo() } return nil } @@ -424,7 +412,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), task.OnSuccess(getResponse, task.Close(writer))); err != nil { common.Interrupt(serverReader) common.Interrupt(serverWriter) - return errors.New("fallback ends").Base(err).AtInfo() + return newError("fallback ends").Base(err).AtInfo() } return nil } @@ -436,21 +424,20 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s Status: log.AccessRejected, Reason: err, }) - err = errors.New("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() + err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() } return err } if err := connection.SetReadDeadline(time.Time{}); err != nil { - errors.LogWarningInner(ctx, err, "unable to set back read deadline") + newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) } - errors.LogInfo(ctx, "received request for ", request.Destination()) + newError("received request for ", request.Destination()).AtInfo().WriteToLog(sid) inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } - inbound.Name = "vless" inbound.User = request.User account := request.User.Account.(*vless.MemoryAccount) @@ -459,31 +446,44 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s // Flow: requestAddons.Flow, } + var netConn net.Conn + var rawConn syscall.RawConn var input *bytes.Reader var rawInput *bytes.Buffer + switch requestAddons.Flow { case vless.XRV: if account.Flow == requestAddons.Flow { - inbound.CanSpliceCopy = 2 switch request.Command { - case protocol.RequestCommandUDP: - return errors.New(requestAddons.Flow + " doesn't support UDP").AtWarning() case protocol.RequestCommandMux: - fallthrough // we will break Mux connections that contain TCP requests + return newError(requestAddons.Flow + " doesn't support Mux").AtWarning() + case protocol.RequestCommandUDP: + return newError(requestAddons.Flow + " doesn't support UDP").AtWarning() case protocol.RequestCommandTCP: var t reflect.Type var p uintptr if tlsConn, ok := iConn.(*tls.Conn); ok { if tlsConn.ConnectionState().Version != gotls.VersionTLS13 { - return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning() + return newError(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning() } + netConn = tlsConn.NetConn() t = reflect.TypeOf(tlsConn.Conn).Elem() p = uintptr(unsafe.Pointer(tlsConn.Conn)) } else if realityConn, ok := iConn.(*reality.Conn); ok { + netConn = realityConn.NetConn() t = reflect.TypeOf(realityConn.Conn).Elem() p = uintptr(unsafe.Pointer(realityConn.Conn)) + } else if _, ok := iConn.(*tls.UConn); ok { + return newError("XTLS only supports UTLS fingerprint for the outbound.").AtWarning() } else { - return errors.New("XTLS only supports TLS and REALITY directly for now.").AtWarning() + return newError("XTLS only supports TCP, mKCP and DomainSocket for now.").AtWarning() + } + if pc, ok := netConn.(*proxyproto.Conn); ok { + netConn = pc.Raw() + // 8192 > 4096, there is no need to process pc's bufReader + } + if sc, ok := netConn.(syscall.Conn); ok { + rawConn, _ = sc.SyscallConn() } i, _ := t.FieldByName("input") r, _ := t.FieldByName("rawInput") @@ -491,15 +491,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) } } else { - return errors.New("account " + account.ID.String() + " is not able to use the flow " + requestAddons.Flow).AtWarning() + return newError(account.ID.String() + " is not able to use " + requestAddons.Flow).AtWarning() } case "": - inbound.CanSpliceCopy = 3 if account.Flow == vless.XRV && (request.Command == protocol.RequestCommandTCP || isMuxAndNotXUDP(request, first)) { - return errors.New("account " + account.ID.String() + " is rejected since the client flow is empty. Note that the pure TLS proxy has certain TLS in TLS characters.").AtWarning() + return newError(account.ID.String() + " is not able to use \"\". Note that the pure TLS proxy has certain TLS in TLS characters.").AtWarning() } default: - return errors.New("unknown request flow " + requestAddons.Flow).AtWarning() + return newError("unknown request flow " + requestAddons.Flow).AtWarning() } if request.Command != protocol.RequestCommandMux { @@ -510,24 +509,27 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s Reason: "", Email: request.User.Email, }) - } else if account.Flow == vless.XRV { - ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP) } sessionPolicy = h.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) - inbound.Timer = timer ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) link, err := dispatcher.Dispatch(ctx, request.Destination()) if err != nil { - return errors.New("failed to dispatch request to ", request.Destination()).Base(err).AtWarning() + return newError("failed to dispatch request to ", request.Destination()).Base(err).AtWarning() } serverReader := link.Reader // .(*pipe.Reader) serverWriter := link.Writer // .(*pipe.Writer) - trafficState := proxy.NewTrafficState(account.ID.Bytes()) + enableXtls := false + isTLS12orAbove := false + isTLS := false + var cipher uint16 = 0 + var remainingServerHello int32 = -1 + numberOfPacketToFilter := 8 + postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) @@ -536,17 +538,22 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s var err error - if requestAddons.Flow == vless.XRV { - ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice - clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1) - err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, true, ctx1) + if rawConn != nil { + var counter stats.Counter + if statConn != nil { + counter = statConn.ReadCounter + } + // TODO enable splice + ctx = session.ContextWithInbound(ctx, nil) + err = encoding.XtlsRead(clientReader, serverWriter, timer, netConn, rawConn, input, rawInput, counter, ctx, account.ID.Bytes(), + &numberOfPacketToFilter, &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello) } else { - // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer + // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)) } if err != nil { - return errors.New("failed to transfer request payload").Base(err).AtInfo() + return newError("failed to transfer request payload").Base(err).AtInfo() } return nil @@ -557,32 +564,45 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection)) if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil { - return errors.New("failed to encode response header").Base(err).AtWarning() + return newError("failed to encode response header").Base(err).AtWarning() } // default: clientWriter := bufferWriter - clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, false, ctx) + clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons) + userUUID := account.ID.Bytes() multiBuffer, err1 := serverReader.ReadMultiBuffer() if err1 != nil { return err1 // ... } + if requestAddons.Flow == vless.XRV { + encoding.XtlsFilterTls(multiBuffer, &numberOfPacketToFilter, &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello, ctx) + multiBuffer = encoding.ReshapeMultiBuffer(ctx, multiBuffer) + for i, b := range multiBuffer { + multiBuffer[i] = encoding.XtlsPadding(b, encoding.CommandPaddingContinue, &userUUID, isTLS, ctx) + } + } if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil { return err // ... } - // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer + // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer if err := bufferWriter.SetBuffered(false); err != nil { - return errors.New("failed to write A response payload").Base(err).AtWarning() + return newError("failed to write A response payload").Base(err).AtWarning() } var err error - if requestAddons.Flow == vless.XRV { - err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, false, ctx) + if rawConn != nil && requestAddons.Flow == vless.XRV { + var counter stats.Counter + if statConn != nil { + counter = statConn.WriteCounter + } + err = encoding.XtlsWrite(serverReader, clientWriter, timer, netConn, counter, ctx, &numberOfPacketToFilter, + &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello) } else { - // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer + // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)) } if err != nil { - return errors.New("failed to transfer response payload").Base(err).AtInfo() + return newError("failed to transfer response payload").Base(err).AtInfo() } // Indicates the end of response payload. switch responseAddons.Flow { @@ -595,7 +615,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), getResponse); err != nil { common.Interrupt(serverReader) common.Interrupt(serverWriter) - return errors.New("connection ends").Base(err).AtInfo() + return newError("connection ends").Base(err).AtInfo() } return nil diff --git a/proxy/vless/outbound/config.pb.go b/proxy/vless/outbound/config.pb.go index 76d2f768..4d1b7938 100644 --- a/proxy/vless/outbound/config.pb.go +++ b/proxy/vless/outbound/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/vless/outbound/config.proto package outbound @@ -31,9 +31,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_vless_outbound_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vless_outbound_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -44,7 +46,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_outbound_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -102,7 +104,7 @@ func file_proxy_vless_outbound_config_proto_rawDescGZIP() []byte { } var file_proxy_vless_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_vless_outbound_config_proto_goTypes = []any{ +var file_proxy_vless_outbound_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.proxy.vless.outbound.Config (*protocol.ServerEndpoint)(nil), // 1: xray.common.protocol.ServerEndpoint } @@ -120,6 +122,20 @@ func file_proxy_vless_outbound_config_proto_init() { if File_proxy_vless_outbound_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_vless_outbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/vless/outbound/errors.generated.go b/proxy/vless/outbound/errors.generated.go new file mode 100644 index 00000000..07966823 --- /dev/null +++ b/proxy/vless/outbound/errors.generated.go @@ -0,0 +1,9 @@ +package outbound + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/outbound/outbound.go b/proxy/vless/outbound/outbound.go index e1a727eb..cde09bad 100644 --- a/proxy/vless/outbound/outbound.go +++ b/proxy/vless/outbound/outbound.go @@ -1,17 +1,19 @@ package outbound +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "bytes" "context" gotls "crypto/tls" "reflect" + "syscall" "time" "unsafe" utls "github.com/refraction-networking/utls" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/retry" @@ -21,7 +23,7 @@ import ( "github.com/xtls/xray-core/common/xudp" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/policy" - "github.com/xtls/xray-core/proxy" + "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless/encoding" "github.com/xtls/xray-core/transport" @@ -51,7 +53,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { for _, rec := range config.Vnext { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { - return nil, errors.New("failed to parse server spec").Base(err).AtError() + return nil, newError("failed to parse server spec").Base(err).AtError() } serverList.AddServer(s) } @@ -69,15 +71,9 @@ func New(ctx context.Context, config *Config) (*Handler, error) { // Process implements proxy.Outbound.Process(). func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified").AtError() - } - ob.Name = "vless" - var rec *protocol.ServerSpec var conn stat.Connection + if err := retry.ExponentialBackoff(5, 200).On(func() error { rec = h.serverPicker.PickServer() var err error @@ -87,16 +83,23 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte } return nil }); err != nil { - return errors.New("failed to find an available destination").Base(err).AtWarning() + return newError("failed to find an available destination").Base(err).AtWarning() } defer conn.Close() iConn := conn - if statConn, ok := iConn.(*stat.CounterConnection); ok { + statConn, ok := iConn.(*stat.CounterConnection) + if ok { iConn = statConn.Connection } - target := ob.Target - errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr()) + + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified").AtError() + } + + target := outbound.Target + newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).AtInfo().WriteToLog(session.ExportIDToError(ctx)) command := protocol.RequestCommandTCP if target.Network == net.Network_UDP { @@ -120,6 +123,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte Flow: account.Flow, } + var netConn net.Conn + var rawConn syscall.RawConn var input *bytes.Reader var rawInput *bytes.Buffer allowUDP443 := false @@ -129,57 +134,56 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte requestAddons.Flow = requestAddons.Flow[:16] fallthrough case vless.XRV: - ob.CanSpliceCopy = 2 switch request.Command { + case protocol.RequestCommandMux: + return newError(requestAddons.Flow + " doesn't support Mux").AtWarning() case protocol.RequestCommandUDP: if !allowUDP443 && request.Port == 443 { - return errors.New("XTLS rejected UDP/443 traffic").AtInfo() + return newError(requestAddons.Flow + " stopped UDP/443").AtInfo() } - case protocol.RequestCommandMux: - fallthrough // let server break Mux connections that contain TCP requests + requestAddons.Flow = "" case protocol.RequestCommandTCP: var t reflect.Type var p uintptr if tlsConn, ok := iConn.(*tls.Conn); ok { + netConn = tlsConn.NetConn() t = reflect.TypeOf(tlsConn.Conn).Elem() p = uintptr(unsafe.Pointer(tlsConn.Conn)) } else if utlsConn, ok := iConn.(*tls.UConn); ok { + netConn = utlsConn.NetConn() t = reflect.TypeOf(utlsConn.Conn).Elem() p = uintptr(unsafe.Pointer(utlsConn.Conn)) } else if realityConn, ok := iConn.(*reality.UConn); ok { + netConn = realityConn.NetConn() t = reflect.TypeOf(realityConn.Conn).Elem() p = uintptr(unsafe.Pointer(realityConn.Conn)) } else { - return errors.New("XTLS only supports TLS and REALITY directly for now.").AtWarning() + return newError("XTLS only supports TCP, mKCP and DomainSocket for now.").AtWarning() + } + if sc, ok := netConn.(syscall.Conn); ok { + rawConn, _ = sc.SyscallConn() } i, _ := t.FieldByName("input") r, _ := t.FieldByName("rawInput") input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset)) rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) } - default: - ob.CanSpliceCopy = 3 - } - - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) } sessionPolicy := h.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, sessionPolicy.Timeouts.ConnectionIdle) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) clientReader := link.Reader // .(*pipe.Reader) clientWriter := link.Writer // .(*pipe.Writer) - trafficState := proxy.NewTrafficState(account.ID.Bytes()) - if request.Command == protocol.RequestCommandUDP && (requestAddons.Flow == vless.XRV || (h.cone && request.Port != 53 && request.Port != 443)) { + enableXtls := false + isTLS12orAbove := false + isTLS := false + var cipher uint16 = 0 + var remainingServerHello int32 = -1 + numberOfPacketToFilter := 8 + + if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 { request.Command = protocol.RequestCommandMux request.Address = net.DomainAddress("v1.mux.cool") request.Port = net.Port(666) @@ -190,18 +194,26 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) if err := encoding.EncodeRequestHeader(bufferWriter, request, requestAddons); err != nil { - return errors.New("failed to encode request header").Base(err).AtWarning() + return newError("failed to encode request header").Base(err).AtWarning() } // default: serverWriter := bufferWriter - serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, true, ctx) + serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons) if request.Command == protocol.RequestCommandMux && request.Port == 666 { - serverWriter = xudp.NewPacketWriter(serverWriter, target, xudp.GetGlobalID(ctx)) + serverWriter = xudp.NewPacketWriter(serverWriter, target) } + userUUID := account.ID.Bytes() timeoutReader, ok := clientReader.(buf.TimeoutReader) if ok { multiBuffer, err1 := timeoutReader.ReadMultiBufferTimeout(time.Millisecond * 500) if err1 == nil { + if requestAddons.Flow == vless.XRV { + encoding.XtlsFilterTls(multiBuffer, &numberOfPacketToFilter, &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello, ctx) + multiBuffer = encoding.ReshapeMultiBuffer(ctx, multiBuffer) + for i, b := range multiBuffer { + multiBuffer[i] = encoding.XtlsPadding(b, encoding.CommandPaddingContinue, &userUUID, isTLS, ctx) + } + } if err := serverWriter.WriteMultiBuffer(multiBuffer); err != nil { return err // ... } @@ -209,38 +221,43 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte return err1 } else if requestAddons.Flow == vless.XRV { mb := make(buf.MultiBuffer, 1) - errors.LogInfo(ctx, "Insert padding with empty content to camouflage VLESS header ", mb.Len()) + mb[0] = encoding.XtlsPadding(nil, encoding.CommandPaddingContinue, &userUUID, true, ctx) // we do a long padding to hide vless header + newError("Insert padding with empty content to camouflage VLESS header ", mb.Len()).WriteToLog(session.ExportIDToError(ctx)) if err := serverWriter.WriteMultiBuffer(mb); err != nil { - return err // ... + return err } } } else { - errors.LogDebug(ctx, "Reader is not timeout reader, will send out vless header separately from first payload") + newError("Reader is not timeout reader, will send out vless header separately from first payload").AtDebug().WriteToLog(session.ExportIDToError(ctx)) } - // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer + // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer if err := bufferWriter.SetBuffered(false); err != nil { - return errors.New("failed to write A request payload").Base(err).AtWarning() + return newError("failed to write A request payload").Base(err).AtWarning() } var err error - if requestAddons.Flow == vless.XRV { + if rawConn != nil && requestAddons.Flow == vless.XRV { if tlsConn, ok := iConn.(*tls.Conn); ok { if tlsConn.ConnectionState().Version != gotls.VersionTLS13 { - return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning() + return newError(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning() } } else if utlsConn, ok := iConn.(*tls.UConn); ok { if utlsConn.ConnectionState().Version != utls.VersionTLS13 { - return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version).AtWarning() + return newError(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version).AtWarning() } } - ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice - err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, true, ctx1) + var counter stats.Counter + if statConn != nil { + counter = statConn.WriteCounter + } + err = encoding.XtlsWrite(clientReader, serverWriter, timer, netConn, counter, ctx, &numberOfPacketToFilter, + &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello) } else { - // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer + // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)) } if err != nil { - return errors.New("failed to transfer request payload").Base(err).AtInfo() + return newError("failed to transfer request payload").Base(err).AtInfo() } // Indicates the end of request payload. @@ -255,42 +272,36 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte responseAddons, err := encoding.DecodeResponseHeader(conn, request) if err != nil { - return errors.New("failed to decode response header").Base(err).AtInfo() + return newError("failed to decode response header").Base(err).AtInfo() } // default: serverReader := buf.NewReader(conn) serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons) - if requestAddons.Flow == vless.XRV { - serverReader = proxy.NewVisionReader(serverReader, trafficState, false, ctx) - } if request.Command == protocol.RequestCommandMux && request.Port == 666 { - if requestAddons.Flow == vless.XRV { - serverReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: serverReader}) - } else { - serverReader = xudp.NewPacketReader(conn) - } + serverReader = xudp.NewPacketReader(conn) } - if requestAddons.Flow == vless.XRV { - err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, false, ctx) + if rawConn != nil { + var counter stats.Counter + if statConn != nil { + counter = statConn.ReadCounter + } + err = encoding.XtlsRead(serverReader, clientWriter, timer, netConn, rawConn, input, rawInput, counter, ctx, account.ID.Bytes(), + &numberOfPacketToFilter, &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello) } else { - // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer + // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)) } if err != nil { - return errors.New("failed to transfer response payload").Base(err).AtInfo() + return newError("failed to transfer response payload").Base(err).AtInfo() } return nil } - if newCtx != nil { - ctx = newCtx - } - if err := task.Run(ctx, postRequest, task.OnSuccess(getResponse, task.Close(clientWriter))); err != nil { - return errors.New("connection ends").Base(err).AtInfo() + return newError("connection ends").Base(err).AtInfo() } return nil diff --git a/proxy/vless/validator.go b/proxy/vless/validator.go index d1356c5f..c294ecef 100644 --- a/proxy/vless/validator.go +++ b/proxy/vless/validator.go @@ -4,33 +4,23 @@ import ( "strings" "sync" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/uuid" ) -type Validator interface { - Get(id uuid.UUID) *protocol.MemoryUser - Add(u *protocol.MemoryUser) error - Del(email string) error - GetByEmail(email string) *protocol.MemoryUser - GetAll() []*protocol.MemoryUser - GetCount() int64 -} - -// MemoryValidator stores valid VLESS users. -type MemoryValidator struct { +// Validator stores valid VLESS users. +type Validator struct { // Considering email's usage here, map + sync.Mutex/RWMutex may have better performance. email sync.Map users sync.Map } // Add a VLESS user, Email must be empty or unique. -func (v *MemoryValidator) Add(u *protocol.MemoryUser) error { +func (v *Validator) Add(u *protocol.MemoryUser) error { if u.Email != "" { _, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u) if loaded { - return errors.New("User ", u.Email, " already exists.") + return newError("User ", u.Email, " already exists.") } } v.users.Store(u.Account.(*MemoryAccount).ID.UUID(), u) @@ -38,14 +28,14 @@ func (v *MemoryValidator) Add(u *protocol.MemoryUser) error { } // Del a VLESS user with a non-empty Email. -func (v *MemoryValidator) Del(e string) error { +func (v *Validator) Del(e string) error { if e == "" { - return errors.New("Email must not be empty.") + return newError("Email must not be empty.") } le := strings.ToLower(e) u, _ := v.email.Load(le) if u == nil { - return errors.New("User ", e, " not found.") + return newError("User ", e, " not found.") } v.email.Delete(le) v.users.Delete(u.(*protocol.MemoryUser).Account.(*MemoryAccount).ID.UUID()) @@ -53,40 +43,10 @@ func (v *MemoryValidator) Del(e string) error { } // Get a VLESS user with UUID, nil if user doesn't exist. -func (v *MemoryValidator) Get(id uuid.UUID) *protocol.MemoryUser { +func (v *Validator) Get(id uuid.UUID) *protocol.MemoryUser { u, _ := v.users.Load(id) if u != nil { return u.(*protocol.MemoryUser) } return nil } - -// Get a VLESS user with email, nil if user doesn't exist. -func (v *MemoryValidator) GetByEmail(email string) *protocol.MemoryUser { - email = strings.ToLower(email) - u, _ := v.email.Load(email) - if u != nil { - return u.(*protocol.MemoryUser) - } - return nil -} - -// Get all users -func (v *MemoryValidator) GetAll() []*protocol.MemoryUser { - var u = make([]*protocol.MemoryUser, 0, 100) - v.email.Range(func(key, value interface{}) bool { - u = append(u, value.(*protocol.MemoryUser)) - return true - }) - return u -} - -// Get users count -func (v *MemoryValidator) GetCount() int64 { - var c int64 = 0 - v.email.Range(func(key, value interface{}) bool { - c++ - return true - }) - return c -} diff --git a/proxy/vless/vless.go b/proxy/vless/vless.go index ed63b641..09827a54 100644 --- a/proxy/vless/vless.go +++ b/proxy/vless/vless.go @@ -5,6 +5,8 @@ // clients with 'socks' for proxying. package vless +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + const ( XRV = "xtls-rprx-vision" ) diff --git a/proxy/vmess/account.go b/proxy/vmess/account.go index df8ba52b..809bca21 100644 --- a/proxy/vmess/account.go +++ b/proxy/vmess/account.go @@ -1,10 +1,9 @@ package vmess import ( - "google.golang.org/protobuf/proto" "strings" - "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/uuid" ) @@ -13,6 +12,8 @@ import ( type MemoryAccount struct { // ID is the main ID of the account. ID *protocol.ID + // AlterIDs are the alternative IDs of the account. + AlterIDs []*protocol.ID // Security type of the account. Used for client connections. Security protocol.SecurityType @@ -20,35 +21,29 @@ type MemoryAccount struct { NoTerminationSignal bool } +// AnyValidID returns an ID that is either the main ID or one of the alternative IDs if any. +func (a *MemoryAccount) AnyValidID() *protocol.ID { + if len(a.AlterIDs) == 0 { + return a.ID + } + return a.AlterIDs[dice.Roll(len(a.AlterIDs))] +} + // Equals implements protocol.Account. func (a *MemoryAccount) Equals(account protocol.Account) bool { vmessAccount, ok := account.(*MemoryAccount) if !ok { return false } + // TODO: handle AlterIds difference return a.ID.Equals(vmessAccount.ID) } -func (a *MemoryAccount) ToProto() proto.Message { - var test = "" - if a.AuthenticatedLengthExperiment { - test = "AuthenticatedLength|" - } - if a.NoTerminationSignal { - test = test + "NoTerminationSignal" - } - return &Account{ - Id: a.ID.String(), - TestsEnabled: test, - SecuritySettings: &protocol.SecurityConfig{Type: a.Security}, - } -} - // AsAccount implements protocol.Account. func (a *Account) AsAccount() (protocol.Account, error) { id, err := uuid.ParseString(a.Id) if err != nil { - return nil, errors.New("failed to parse ID").Base(err).AtError() + return nil, newError("failed to parse ID").Base(err).AtError() } protoID := protocol.NewID(id) var AuthenticatedLength, NoTerminationSignal bool @@ -60,6 +55,7 @@ func (a *Account) AsAccount() (protocol.Account, error) { } return &MemoryAccount{ ID: protoID, + AlterIDs: protocol.NewAlterIDs(protoID, uint16(a.AlterId)), Security: a.SecuritySettings.GetSecurityType(), AuthenticatedLengthExperiment: AuthenticatedLength, NoTerminationSignal: NoTerminationSignal, diff --git a/proxy/vmess/account.pb.go b/proxy/vmess/account.pb.go index 44a8f73a..0f2f6159 100644 --- a/proxy/vmess/account.pb.go +++ b/proxy/vmess/account.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/vmess/account.proto package vmess @@ -29,6 +29,8 @@ type Account struct { // ID of the account, in the form of a UUID, e.g., // "66ad4540-b58c-4ad2-9926-ea63445a9b57". Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Number of alternative IDs. Client and server must share the same number. + AlterId uint32 `protobuf:"varint,2,opt,name=alter_id,json=alterId,proto3" json:"alter_id,omitempty"` // Security settings. Only applies to client side. SecuritySettings *protocol.SecurityConfig `protobuf:"bytes,3,opt,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` // Define tests enabled for this account @@ -37,9 +39,11 @@ type Account struct { func (x *Account) Reset() { *x = Account{} - mi := &file_proxy_vmess_account_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vmess_account_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Account) String() string { @@ -50,7 +54,7 @@ func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_account_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -72,6 +76,13 @@ func (x *Account) GetId() string { return "" } +func (x *Account) GetAlterId() uint32 { + if x != nil { + return x.AlterId + } + return 0 +} + func (x *Account) GetSecuritySettings() *protocol.SecurityConfig { if x != nil { return x.SecuritySettings @@ -93,22 +104,24 @@ var file_proxy_vmess_account_proto_rawDesc = []byte{ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x1a, 0x1d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x91, 0x01, 0x0a, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xac, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x51, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, - 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, - 0x6d, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x6c, 0x74, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x51, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, + 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x73, 0x5f, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, + 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x52, 0x0a, 0x14, 0x63, + 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, + 0x65, 0x73, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, + 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6d, 0x65, 0x73, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -124,7 +137,7 @@ func file_proxy_vmess_account_proto_rawDescGZIP() []byte { } var file_proxy_vmess_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_vmess_account_proto_goTypes = []any{ +var file_proxy_vmess_account_proto_goTypes = []interface{}{ (*Account)(nil), // 0: xray.proxy.vmess.Account (*protocol.SecurityConfig)(nil), // 1: xray.common.protocol.SecurityConfig } @@ -142,6 +155,20 @@ func file_proxy_vmess_account_proto_init() { if File_proxy_vmess_account_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_vmess_account_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/vmess/account.proto b/proxy/vmess/account.proto index 3fac6399..98dc681e 100644 --- a/proxy/vmess/account.proto +++ b/proxy/vmess/account.proto @@ -12,6 +12,8 @@ message Account { // ID of the account, in the form of a UUID, e.g., // "66ad4540-b58c-4ad2-9926-ea63445a9b57". string id = 1; + // Number of alternative IDs. Client and server must share the same number. + uint32 alter_id = 2; // Security settings. Only applies to client side. xray.common.protocol.SecurityConfig security_settings = 3; // Define tests enabled for this account diff --git a/proxy/vmess/encoding/auth.go b/proxy/vmess/encoding/auth.go index 99bdaa49..09689cea 100644 --- a/proxy/vmess/encoding/auth.go +++ b/proxy/vmess/encoding/auth.go @@ -17,7 +17,6 @@ func Authenticate(b []byte) uint32 { return fnv1hash.Sum32() } -// [DEPRECATED 2023-06] type NoOpAuthenticator struct{} func (NoOpAuthenticator) NonceSize() int { @@ -38,6 +37,34 @@ func (NoOpAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([] return append(dst[:0], ciphertext...), nil } +// FnvAuthenticator is an AEAD based on Fnv hash. +type FnvAuthenticator struct{} + +// NonceSize implements AEAD.NonceSize(). +func (*FnvAuthenticator) NonceSize() int { + return 0 +} + +// Overhead impelements AEAD.Overhead(). +func (*FnvAuthenticator) Overhead() int { + return 4 +} + +// Seal implements AEAD.Seal(). +func (*FnvAuthenticator) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + dst = append(dst, 0, 0, 0, 0) + binary.BigEndian.PutUint32(dst, Authenticate(plaintext)) + return append(dst, plaintext...) +} + +// Open implements AEAD.Open(). +func (*FnvAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if binary.BigEndian.Uint32(ciphertext[:4]) != Authenticate(ciphertext[4:]) { + return dst, newError("invalid authentication") + } + return append(dst, ciphertext[4:]...), nil +} + // GenerateChacha20Poly1305Key generates a 32-byte key from a given 16-byte array. func GenerateChacha20Poly1305Key(b []byte) []byte { key := make([]byte, 32) diff --git a/proxy/vmess/encoding/auth_test.go b/proxy/vmess/encoding/auth_test.go new file mode 100644 index 00000000..ae83076f --- /dev/null +++ b/proxy/vmess/encoding/auth_test.go @@ -0,0 +1,26 @@ +package encoding_test + +import ( + "crypto/rand" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/xtls/xray-core/common" + . "github.com/xtls/xray-core/proxy/vmess/encoding" +) + +func TestFnvAuth(t *testing.T) { + fnvAuth := new(FnvAuthenticator) + + expectedText := make([]byte, 256) + _, err := rand.Read(expectedText) + common.Must(err) + + buffer := make([]byte, 512) + b := fnvAuth.Seal(buffer[:0], nil, expectedText, nil) + b, err = fnvAuth.Open(buffer[:0], nil, b, nil) + common.Must(err) + if r := cmp.Diff(b, expectedText); r != "" { + t.Error(r) + } +} diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go index d678646b..ba08843e 100644 --- a/proxy/vmess/encoding/client.go +++ b/proxy/vmess/encoding/client.go @@ -5,9 +5,11 @@ import ( "context" "crypto/aes" "crypto/cipher" + "crypto/md5" "crypto/rand" "crypto/sha256" "encoding/binary" + "hash" "hash/fnv" "io" @@ -17,15 +19,25 @@ import ( "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/drain" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/proxy/vmess" vmessaead "github.com/xtls/xray-core/proxy/vmess/aead" "golang.org/x/crypto/chacha20poly1305" ) +func hashTimestamp(h hash.Hash, t protocol.Timestamp) []byte { + common.Must2(serial.WriteUint64(h, uint64(t))) + common.Must2(serial.WriteUint64(h, uint64(t))) + common.Must2(serial.WriteUint64(h, uint64(t))) + common.Must2(serial.WriteUint64(h, uint64(t))) + return h.Sum(nil) +} + // ClientSession stores connection session info for VMess client. type ClientSession struct { + isAEAD bool + idHash protocol.IDHash requestBodyKey [16]byte requestBodyIV [16]byte responseBodyKey [16]byte @@ -37,8 +49,11 @@ type ClientSession struct { } // NewClientSession creates a new ClientSession. -func NewClientSession(ctx context.Context, behaviorSeed int64) *ClientSession { - session := &ClientSession{} +func NewClientSession(ctx context.Context, isAEAD bool, idHash protocol.IDHash, behaviorSeed int64) *ClientSession { + session := &ClientSession{ + isAEAD: isAEAD, + idHash: idHash, + } randomBytes := make([]byte, 33) // 16 + 16 + 1 common.Must2(rand.Read(randomBytes)) @@ -46,15 +61,20 @@ func NewClientSession(ctx context.Context, behaviorSeed int64) *ClientSession { copy(session.requestBodyIV[:], randomBytes[16:32]) session.responseHeader = randomBytes[32] - BodyKey := sha256.Sum256(session.requestBodyKey[:]) - copy(session.responseBodyKey[:], BodyKey[:16]) - BodyIV := sha256.Sum256(session.requestBodyIV[:]) - copy(session.responseBodyIV[:], BodyIV[:16]) + if !session.isAEAD { + session.responseBodyKey = md5.Sum(session.requestBodyKey[:]) + session.responseBodyIV = md5.Sum(session.requestBodyIV[:]) + } else { + BodyKey := sha256.Sum256(session.requestBodyKey[:]) + copy(session.responseBodyKey[:], BodyKey[:16]) + BodyIV := sha256.Sum256(session.requestBodyIV[:]) + copy(session.responseBodyIV[:], BodyIV[:16]) + } { var err error session.readDrainer, err = drain.NewBehaviorSeedLimitedDrainer(behaviorSeed, 18, 3266, 64) if err != nil { - errors.LogInfoInner(ctx, err, "unable to initialize drainer") + newError("unable to initialize drainer").Base(err).WriteToLog() session.readDrainer = drain.NewNopDrainer() } } @@ -63,7 +83,13 @@ func NewClientSession(ctx context.Context, behaviorSeed int64) *ClientSession { } func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) error { + timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)() account := header.User.Account.(*vmess.MemoryAccount) + if !c.isAEAD { + idHash := c.idHash(account.AnyValidID().Bytes()) + common.Must2(serial.WriteUint64(idHash, uint64(timestamp))) + common.Must2(writer.Write(idHash.Sum(nil))) + } buffer := buf.New() defer buffer.Release() @@ -80,7 +106,7 @@ func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ if header.Command != protocol.RequestCommandMux { if err := addrParser.WriteAddressPort(buffer, header.Address, header.Port); err != nil { - return errors.New("failed to writer address and port").Base(err) + return newError("failed to writer address and port").Base(err) } } @@ -95,10 +121,17 @@ func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ fnv1a.Sum(hashBytes[:0]) } - var fixedLengthCmdKey [16]byte - copy(fixedLengthCmdKey[:], account.ID.CmdKey()) - vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes()) - common.Must2(io.Copy(writer, bytes.NewReader(vmessout))) + if !c.isAEAD { + iv := hashTimestamp(md5.New(), timestamp) + aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv) + aesStream.XORKeyStream(buffer.Bytes(), buffer.Bytes()) + common.Must2(writer.Write(buffer.Bytes())) + } else { + var fixedLengthCmdKey [16]byte + copy(fixedLengthCmdKey[:], account.ID.CmdKey()) + vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes()) + common.Must2(io.Copy(writer, bytes.NewReader(vmessout))) + } return nil } @@ -113,7 +146,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") + return nil, newError("invalid option: RequestOptionGlobalPadding") } } @@ -132,6 +165,19 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write } return buf.NewWriter(writer), nil + case protocol.SecurityType_LEGACY: + aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey[:], c.requestBodyIV[:]) + cryptionWriter := crypto.NewCryptionWriter(aesStream, writer) + if request.Option.Has(protocol.RequestOptionChunkStream) { + auth := &crypto.AEADAuthenticator{ + AEAD: new(FnvAuthenticator), + NonceGenerator: crypto.GenerateEmptyBytes(), + AdditionalDataGenerator: crypto.GenerateEmptyBytes(), + } + return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding), nil + } + + return &buf.SequentialWriter{Writer: cryptionWriter}, nil case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(c.requestBodyKey[:]) auth := &crypto.AEADAuthenticator{ @@ -174,64 +220,69 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil default: - return nil, errors.New("invalid option: Security") + return nil, newError("invalid option: Security") } } func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) { - aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) - aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] + if !c.isAEAD { + aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:]) + c.responseReader = crypto.NewCryptionReader(aesStream, reader) + } else { + aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) + aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] - aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) - aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) + aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) + aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) - var aeadEncryptedResponseHeaderLength [18]byte - var decryptedResponseHeaderLength int - var decryptedResponseHeaderLengthBinaryDeserializeBuffer uint16 + var aeadEncryptedResponseHeaderLength [18]byte + var decryptedResponseHeaderLength int + var decryptedResponseHeaderLengthBinaryDeserializeBuffer uint16 - if n, err := io.ReadFull(reader, aeadEncryptedResponseHeaderLength[:]); err != nil { - c.readDrainer.AcknowledgeReceive(n) - return nil, drain.WithError(c.readDrainer, reader, errors.New("Unable to Read Header Len").Base(err)) - } else { // nolint: golint - c.readDrainer.AcknowledgeReceive(n) - } - if decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil); err != nil { - return nil, drain.WithError(c.readDrainer, reader, errors.New("Failed To Decrypt Length").Base(err)) - } else { // nolint: golint - common.Must(binary.Read(bytes.NewReader(decryptedResponseHeaderLengthBinaryBuffer), binary.BigEndian, &decryptedResponseHeaderLengthBinaryDeserializeBuffer)) - decryptedResponseHeaderLength = int(decryptedResponseHeaderLengthBinaryDeserializeBuffer) - } + if n, err := io.ReadFull(reader, aeadEncryptedResponseHeaderLength[:]); err != nil { + c.readDrainer.AcknowledgeReceive(n) + return nil, drain.WithError(c.readDrainer, reader, newError("Unable to Read Header Len").Base(err)) + } else { // nolint: golint + c.readDrainer.AcknowledgeReceive(n) + } + if decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil); err != nil { + return nil, drain.WithError(c.readDrainer, reader, newError("Failed To Decrypt Length").Base(err)) + } else { // nolint: golint + common.Must(binary.Read(bytes.NewReader(decryptedResponseHeaderLengthBinaryBuffer), binary.BigEndian, &decryptedResponseHeaderLengthBinaryDeserializeBuffer)) + decryptedResponseHeaderLength = int(decryptedResponseHeaderLengthBinaryDeserializeBuffer) + } - aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) - aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] + aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) + aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] - aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) - aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) + aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) + aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) - encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16) + encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16) - if n, err := io.ReadFull(reader, encryptedResponseHeaderBuffer); err != nil { - c.readDrainer.AcknowledgeReceive(n) - return nil, drain.WithError(c.readDrainer, reader, errors.New("Unable to Read Header Data").Base(err)) - } else { // nolint: golint - c.readDrainer.AcknowledgeReceive(n) - } + if n, err := io.ReadFull(reader, encryptedResponseHeaderBuffer); err != nil { + c.readDrainer.AcknowledgeReceive(n) + return nil, drain.WithError(c.readDrainer, reader, newError("Unable to Read Header Data").Base(err)) + } else { // nolint: golint + c.readDrainer.AcknowledgeReceive(n) + } - if decryptedResponseHeaderBuffer, err := aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil); err != nil { - return nil, drain.WithError(c.readDrainer, reader, errors.New("Failed To Decrypt Payload").Base(err)) - } else { // nolint: golint - c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer) + if decryptedResponseHeaderBuffer, err := aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil); err != nil { + return nil, drain.WithError(c.readDrainer, reader, newError("Failed To Decrypt Payload").Base(err)) + } else { // nolint: golint + c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer) + } } buffer := buf.StackNew() defer buffer.Release() if _, err := buffer.ReadFullFrom(c.responseReader, 4); err != nil { - return nil, errors.New("failed to read response header").Base(err).AtWarning() + return nil, newError("failed to read response header").Base(err).AtWarning() } if buffer.Byte(0) != c.responseHeader { - return nil, errors.New("unexpected response header. Expecting ", int(c.responseHeader), " but actually ", int(buffer.Byte(0))) + return nil, newError("unexpected response header. Expecting ", int(c.responseHeader), " but actually ", int(buffer.Byte(0))) } header := &protocol.ResponseHeader{ @@ -244,15 +295,17 @@ func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon buffer.Clear() if _, err := buffer.ReadFullFrom(c.responseReader, dataLen); err != nil { - return nil, errors.New("failed to read response command").Base(err) + return nil, newError("failed to read response command").Base(err) } command, err := UnmarshalCommand(cmdID, buffer.Bytes()) if err == nil { header.Command = command } } - aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:]) - c.responseReader = crypto.NewCryptionReader(aesStream, reader) + if c.isAEAD { + aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:]) + c.responseReader = crypto.NewCryptionReader(aesStream, reader) + } return header, nil } @@ -266,7 +319,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") + return nil, newError("invalid option: RequestOptionGlobalPadding") } } @@ -287,6 +340,17 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read } return buf.NewReader(reader), nil + case protocol.SecurityType_LEGACY: + if request.Option.Has(protocol.RequestOptionChunkStream) { + auth := &crypto.AEADAuthenticator{ + AEAD: new(FnvAuthenticator), + NonceGenerator: crypto.GenerateEmptyBytes(), + AdditionalDataGenerator: crypto.GenerateEmptyBytes(), + } + return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding), nil + } + + return buf.NewReader(c.responseReader), nil case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(c.responseBodyKey[:]) @@ -329,7 +393,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil default: - return nil, errors.New("invalid option: Security") + return nil, newError("invalid option: Security") } } diff --git a/proxy/vmess/encoding/commands.go b/proxy/vmess/encoding/commands.go index cdab871b..1b420021 100644 --- a/proxy/vmess/encoding/commands.go +++ b/proxy/vmess/encoding/commands.go @@ -6,7 +6,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" @@ -14,11 +13,11 @@ import ( ) var ( - ErrCommandTooLarge = errors.New("Command too large.") - ErrCommandTypeMismatch = errors.New("Command type mismatch.") - ErrInvalidAuth = errors.New("Invalid auth.") - ErrInsufficientLength = errors.New("Insufficient length.") - ErrUnknownCommand = errors.New("Unknown command.") + ErrCommandTooLarge = newError("Command too large.") + ErrCommandTypeMismatch = newError("Command type mismatch.") + ErrInvalidAuth = newError("Invalid auth.") + ErrInsufficientLength = newError("Insufficient length.") + ErrUnknownCommand = newError("Unknown command.") ) func MarshalCommand(command interface{}, writer io.Writer) error { @@ -102,7 +101,7 @@ func (f *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.Wri idBytes := cmd.ID.Bytes() common.Must2(writer.Write(idBytes)) - common.Must2(serial.WriteUint16(writer, 0)) // compatible with legacy alterId + common.Must2(serial.WriteUint16(writer, cmd.AlterIds)) common.Must2(writer.Write([]byte{byte(cmd.Level)})) common.Must2(writer.Write([]byte{cmd.ValidMin})) @@ -131,7 +130,12 @@ func (f *CommandSwitchAccountFactory) Unmarshal(data []byte) (interface{}, error return nil, ErrInsufficientLength } cmd.ID, _ = uuid.ParseBytes(data[idStart : idStart+16]) - levelStart := idStart + 16 + 2 + alterIDStart := idStart + 16 + if len(data) < alterIDStart+2 { + return nil, ErrInsufficientLength + } + cmd.AlterIds = binary.BigEndian.Uint16(data[alterIDStart : alterIDStart+2]) + levelStart := alterIDStart + 2 if len(data) < levelStart+1 { return nil, ErrInsufficientLength } diff --git a/proxy/vmess/encoding/commands_test.go b/proxy/vmess/encoding/commands_test.go index c5415959..17892bb6 100644 --- a/proxy/vmess/encoding/commands_test.go +++ b/proxy/vmess/encoding/commands_test.go @@ -16,6 +16,7 @@ func TestSwitchAccount(t *testing.T) { sa := &protocol.CommandSwitchAccount{ Port: 1234, ID: uuid.New(), + AlterIds: 1024, Level: 128, ValidMin: 16, } @@ -39,6 +40,7 @@ func TestSwitchAccountBugOffByOne(t *testing.T) { sa := &protocol.CommandSwitchAccount{ Port: 1234, ID: uuid.New(), + AlterIds: 1024, Level: 128, ValidMin: 16, } diff --git a/proxy/vmess/encoding/encoding.go b/proxy/vmess/encoding/encoding.go index 45c5d207..6a93dd12 100644 --- a/proxy/vmess/encoding/encoding.go +++ b/proxy/vmess/encoding/encoding.go @@ -5,6 +5,8 @@ import ( "github.com/xtls/xray-core/common/protocol" ) +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + const ( Version = byte(1) ) diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index ddae29e1..a8fd8f3a 100644 --- a/proxy/vmess/encoding/encoding_test.go +++ b/proxy/vmess/encoding/encoding_test.go @@ -41,7 +41,7 @@ func TestRequestSerialization(t *testing.T) { } buffer := buf.New() - client := NewClientSession(context.TODO(), 0) + client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) buffer2 := buf.New() @@ -50,7 +50,7 @@ func TestRequestSerialization(t *testing.T) { sessionHistory := NewSessionHistory() defer common.Close(sessionHistory) - userValidator := vmess.NewTimedUserValidator() + userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) defer common.Close(userValidator) @@ -90,7 +90,7 @@ func TestInvalidRequest(t *testing.T) { } buffer := buf.New() - client := NewClientSession(context.TODO(), 0) + client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) buffer2 := buf.New() @@ -99,7 +99,7 @@ func TestInvalidRequest(t *testing.T) { sessionHistory := NewSessionHistory() defer common.Close(sessionHistory) - userValidator := vmess.NewTimedUserValidator() + userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) defer common.Close(userValidator) @@ -130,7 +130,7 @@ func TestMuxRequest(t *testing.T) { } buffer := buf.New() - client := NewClientSession(context.TODO(), 0) + client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) buffer2 := buf.New() @@ -139,7 +139,7 @@ func TestMuxRequest(t *testing.T) { sessionHistory := NewSessionHistory() defer common.Close(sessionHistory) - userValidator := vmess.NewTimedUserValidator() + userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) defer common.Close(userValidator) diff --git a/proxy/vmess/encoding/errors.generated.go b/proxy/vmess/encoding/errors.generated.go new file mode 100644 index 00000000..267711d9 --- /dev/null +++ b/proxy/vmess/encoding/errors.generated.go @@ -0,0 +1,9 @@ +package encoding + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 99e7abc9..4bdb6fb7 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/md5" "crypto/sha256" "encoding/binary" "hash/fnv" @@ -16,7 +17,6 @@ import ( "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/drain" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/task" @@ -76,7 +76,7 @@ func (h *SessionHistory) removeExpiredEntries() error { defer h.Unlock() if len(h.cache) == 0 { - return errors.New("nothing to do") + return newError("nothing to do") } for session, expire := range h.cache { @@ -102,6 +102,10 @@ type ServerSession struct { responseBodyIV [16]byte responseWriter io.Writer responseHeader byte + + isAEADRequest bool + + isAEADForced bool } // NewServerSession creates a new ServerSession, using the given UserValidator. @@ -113,12 +117,17 @@ func NewServerSession(validator *vmess.TimedUserValidator, sessionHistory *Sessi } } +// SetAEADForced sets isAEADForced for a ServerSession. +func (s *ServerSession) SetAEADForced(isAEADForced bool) { + s.isAEADForced = isAEADForced +} + func parseSecurityType(b byte) protocol.SecurityType { if _, f := protocol.SecurityType_name[int32(b)]; f { st := protocol.SecurityType(b) // For backward compatibility. if st == protocol.SecurityType_UNKNOWN { - st = protocol.SecurityType_AUTO + st = protocol.SecurityType_LEGACY } return st } @@ -131,7 +140,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(s.userValidator.GetBehaviorSeed()), 16+38, 3266, 64) if err != nil { - return nil, errors.New("failed to initialize drainer").Base(err) + return nil, newError("failed to initialize drainer").Base(err) } drainConnection := func(e error) error { @@ -148,7 +157,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr }() if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil { - return nil, errors.New("failed to read request header").Base(err) + return nil, newError("failed to read request header").Base(err) } var decryptor io.Reader @@ -168,20 +177,40 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr if errorReason != nil { if shouldDrain { drainer.AcknowledgeReceive(bytesRead) - return nil, drainConnection(errors.New("AEAD read failed").Base(errorReason)) + return nil, drainConnection(newError("AEAD read failed").Base(errorReason)) } else { - return nil, drainConnection(errors.New("AEAD read failed, drain skipped").Base(errorReason)) + return nil, drainConnection(newError("AEAD read failed, drain skipped").Base(errorReason)) } } decryptor = bytes.NewReader(aeadData) + s.isAEADRequest = true + + case errorAEAD == vmessaead.ErrNotFound: + userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes()) + if !valid || userValidationError != nil { + return nil, drainConnection(newError("invalid user").Base(userValidationError)) + } + if s.isAEADForced { + return nil, drainConnection(newError("invalid user: VMessAEAD is enforced and a non VMessAEAD connection is received. You can still disable this security feature with environment variable xray.vmess.aead.forced = false . You will not be able to enable legacy header workaround in the future.")) + } + if s.userValidator.ShouldShowLegacyWarn() { + newError("Critical Warning: potentially invalid user: a non VMessAEAD connection is received. From 2022 Jan 1st, this kind of connection will be rejected by default. You should update or replace your client software now. This message will not be shown for further violation on this inbound.").AtWarning().WriteToLog() + } + user = userLegacy + iv := hashTimestamp(md5.New(), timestamp) + vmessAccount = userLegacy.Account.(*vmess.MemoryAccount) + + aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv) + decryptor = crypto.NewCryptionReader(aesStream, reader) + default: - return nil, drainConnection(errors.New("invalid user").Base(errorAEAD)) + return nil, drainConnection(newError("invalid user").Base(errorAEAD)) } drainer.AcknowledgeReceive(int(buffer.Len())) buffer.Clear() if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil { - return nil, errors.New("failed to read request header").Base(err) + return nil, newError("failed to read request header").Base(err) } request := &protocol.RequestHeader{ @@ -196,7 +225,15 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr sid.key = s.requestBodyKey sid.nonce = s.requestBodyIV if !s.sessionHistory.addIfNotExits(sid) { - return nil, errors.New("duplicated session id, possibly under replay attack, but this is a AEAD request") + if !s.isAEADRequest { + drainErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) + if drainErr != nil { + return nil, drainConnection(newError("duplicated session id, possibly under replay attack, and failed to taint userHash").Base(drainErr)) + } + return nil, drainConnection(newError("duplicated session id, possibly under replay attack, userHash tainted")) + } else { + return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request") + } } s.responseHeader = buffer.Byte(33) // 1 byte @@ -220,12 +257,26 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr if paddingLen > 0 { if _, err := buffer.ReadFullFrom(decryptor, int32(paddingLen)); err != nil { - return nil, errors.New("failed to read padding").Base(err) + if !s.isAEADRequest { + burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) + if burnErr != nil { + return nil, newError("failed to read padding, failed to taint userHash").Base(burnErr).Base(err) + } + return nil, newError("failed to read padding, userHash tainted").Base(err) + } + return nil, newError("failed to read padding").Base(err) } } if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil { - return nil, errors.New("failed to read checksum").Base(err) + if !s.isAEADRequest { + burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) + if burnErr != nil { + return nil, newError("failed to read checksum, failed to taint userHash").Base(burnErr).Base(err) + } + return nil, newError("failed to read checksum, userHash tainted").Base(err) + } + return nil, newError("failed to read checksum").Base(err) } fnv1a := fnv.New32a() @@ -234,15 +285,25 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4)) if actualHash != expectedHash { - return nil, errors.New("invalid auth, but this is a AEAD request") + if !s.isAEADRequest { + Autherr := newError("invalid auth, legacy userHash tainted") + burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) + if burnErr != nil { + Autherr = newError("invalid auth, can't taint legacy userHash").Base(burnErr) + } + // It is possible that we are under attack described in https://github.com/xray/xray-core/issues/2523 + return nil, drainConnection(Autherr) + } else { + return nil, newError("invalid auth, but this is a AEAD request") + } } if request.Address == nil { - return nil, errors.New("invalid remote address") + return nil, newError("invalid remote address") } if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO { - return nil, errors.New("unknown security type: ", request.Security) + return nil, newError("unknown security type: ", request.Security) } return request, nil @@ -259,7 +320,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") + return nil, newError("invalid option: RequestOptionGlobalPadding") } } @@ -279,6 +340,19 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade } return buf.NewReader(reader), nil + case protocol.SecurityType_LEGACY: + aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:]) + cryptionReader := crypto.NewCryptionReader(aesStream, reader) + if request.Option.Has(protocol.RequestOptionChunkStream) { + auth := &crypto.AEADAuthenticator{ + AEAD: new(FnvAuthenticator), + NonceGenerator: crypto.GenerateEmptyBytes(), + AdditionalDataGenerator: crypto.GenerateEmptyBytes(), + } + return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding), nil + } + return buf.NewReader(cryptionReader), nil + case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(s.requestBodyKey[:]) auth := &crypto.AEADAuthenticator{ @@ -322,24 +396,32 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil default: - return nil, errors.New("invalid option: Security") + return nil, newError("invalid option: Security") } } // EncodeResponseHeader writes encoded response header into the given writer. func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) { var encryptionWriter io.Writer - BodyKey := sha256.Sum256(s.requestBodyKey[:]) - copy(s.responseBodyKey[:], BodyKey[:16]) - BodyIV := sha256.Sum256(s.requestBodyIV[:]) - copy(s.responseBodyIV[:], BodyIV[:16]) + if !s.isAEADRequest { + s.responseBodyKey = md5.Sum(s.requestBodyKey[:]) + s.responseBodyIV = md5.Sum(s.requestBodyIV[:]) + } else { + BodyKey := sha256.Sum256(s.requestBodyKey[:]) + copy(s.responseBodyKey[:], BodyKey[:16]) + BodyIV := sha256.Sum256(s.requestBodyIV[:]) + copy(s.responseBodyIV[:], BodyIV[:16]) + } aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:]) encryptionWriter = crypto.NewCryptionWriter(aesStream, writer) s.responseWriter = encryptionWriter aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil) - encryptionWriter = aeadEncryptedHeaderBuffer + + if s.isAEADRequest { + encryptionWriter = aeadEncryptedHeaderBuffer + } common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)})) err := MarshalCommand(header.Command, encryptionWriter) @@ -347,29 +429,31 @@ func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr common.Must2(encryptionWriter.Write([]byte{0x00, 0x00})) } - aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) - aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] + if s.isAEADRequest { + aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) + aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] - aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) - aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) + aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) + aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) - aeadResponseHeaderLengthEncryptionBuffer := bytes.NewBuffer(nil) + aeadResponseHeaderLengthEncryptionBuffer := bytes.NewBuffer(nil) - decryptedResponseHeaderLengthBinaryDeserializeBuffer := uint16(aeadEncryptedHeaderBuffer.Len()) + decryptedResponseHeaderLengthBinaryDeserializeBuffer := uint16(aeadEncryptedHeaderBuffer.Len()) - common.Must(binary.Write(aeadResponseHeaderLengthEncryptionBuffer, binary.BigEndian, decryptedResponseHeaderLengthBinaryDeserializeBuffer)) + common.Must(binary.Write(aeadResponseHeaderLengthEncryptionBuffer, binary.BigEndian, decryptedResponseHeaderLengthBinaryDeserializeBuffer)) - AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil) - common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength))) + AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil) + common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength))) - aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) - aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] + aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) + aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] - aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) - aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) + aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) + aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) - aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil) - common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload))) + aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil) + common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload))) + } } // EncodeResponseBody returns a Writer that auto-encrypt content written by caller. @@ -383,7 +467,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { - return nil, errors.New("invalid option: RequestOptionGlobalPadding") + return nil, newError("invalid option: RequestOptionGlobalPadding") } } @@ -403,6 +487,17 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ } return buf.NewWriter(writer), nil + case protocol.SecurityType_LEGACY: + if request.Option.Has(protocol.RequestOptionChunkStream) { + auth := &crypto.AEADAuthenticator{ + AEAD: new(FnvAuthenticator), + NonceGenerator: crypto.GenerateEmptyBytes(), + AdditionalDataGenerator: crypto.GenerateEmptyBytes(), + } + return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding), nil + } + return &buf.SequentialWriter{Writer: s.responseWriter}, nil + case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(s.responseBodyKey[:]) auth := &crypto.AEADAuthenticator{ @@ -446,6 +541,6 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil default: - return nil, errors.New("invalid option: Security") + return nil, newError("invalid option: Security") } } diff --git a/proxy/vmess/errors.generated.go b/proxy/vmess/errors.generated.go new file mode 100644 index 00000000..29d69d74 --- /dev/null +++ b/proxy/vmess/errors.generated.go @@ -0,0 +1,9 @@ +package vmess + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vmess/inbound/config.pb.go b/proxy/vmess/inbound/config.pb.go index 4aa259e8..fc7bf36f 100644 --- a/proxy/vmess/inbound/config.pb.go +++ b/proxy/vmess/inbound/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/vmess/inbound/config.proto package inbound @@ -31,9 +31,11 @@ type DetourConfig struct { func (x *DetourConfig) Reset() { *x = DetourConfig{} - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vmess_inbound_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DetourConfig) String() string { @@ -44,7 +46,7 @@ func (*DetourConfig) ProtoMessage() {} func (x *DetourConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_inbound_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -71,14 +73,17 @@ type DefaultConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Level uint32 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"` + AlterId uint32 `protobuf:"varint,1,opt,name=alter_id,json=alterId,proto3" json:"alter_id,omitempty"` + Level uint32 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"` } func (x *DefaultConfig) Reset() { *x = DefaultConfig{} - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vmess_inbound_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DefaultConfig) String() string { @@ -89,7 +94,7 @@ func (*DefaultConfig) ProtoMessage() {} func (x *DefaultConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_inbound_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -104,6 +109,13 @@ func (*DefaultConfig) Descriptor() ([]byte, []int) { return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{1} } +func (x *DefaultConfig) GetAlterId() uint32 { + if x != nil { + return x.AlterId + } + return 0 +} + func (x *DefaultConfig) GetLevel() uint32 { if x != nil { return x.Level @@ -116,16 +128,19 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` - Default *DefaultConfig `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"` - Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour,proto3" json:"detour,omitempty"` // 4 is for legacy setting + User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` + Default *DefaultConfig `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"` + Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour,proto3" json:"detour,omitempty"` + SecureEncryptionOnly bool `protobuf:"varint,4,opt,name=secure_encryption_only,json=secureEncryptionOnly,proto3" json:"secure_encryption_only,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_vmess_inbound_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vmess_inbound_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -136,7 +151,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_inbound_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -172,6 +187,13 @@ func (x *Config) GetDetour() *DetourConfig { return nil } +func (x *Config) GetSecureEncryptionOnly() bool { + if x != nil { + return x.SecureEncryptionOnly + } + return false +} + var File_proxy_vmess_inbound_config_proto protoreflect.FileDescriptor var file_proxy_vmess_inbound_config_proto_rawDesc = []byte{ @@ -182,29 +204,34 @@ var file_proxy_vmess_inbound_config_proto_rawDesc = []byte{ 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0d, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, - 0xbb, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x07, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x3e, 0x0a, - 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, - 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x42, 0x6a, 0x0a, - 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, - 0x2d, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, - 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6d, 0x65, 0x73, - 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x6c, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0xf1, 0x01, 0x0a, 0x06, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x6f, + 0x75, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x6e, + 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x6e, 0x6c, 0x79, 0x42, 0x6a, + 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, + 0x5a, 0x2d, 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, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, + 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6d, 0x65, + 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -220,7 +247,7 @@ func file_proxy_vmess_inbound_config_proto_rawDescGZIP() []byte { } var file_proxy_vmess_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_proxy_vmess_inbound_config_proto_goTypes = []any{ +var file_proxy_vmess_inbound_config_proto_goTypes = []interface{}{ (*DetourConfig)(nil), // 0: xray.proxy.vmess.inbound.DetourConfig (*DefaultConfig)(nil), // 1: xray.proxy.vmess.inbound.DefaultConfig (*Config)(nil), // 2: xray.proxy.vmess.inbound.Config @@ -242,6 +269,44 @@ func file_proxy_vmess_inbound_config_proto_init() { if File_proxy_vmess_inbound_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_vmess_inbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetourConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_vmess_inbound_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DefaultConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_vmess_inbound_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/vmess/inbound/config.proto b/proxy/vmess/inbound/config.proto index 7da1d581..9fc7e36f 100644 --- a/proxy/vmess/inbound/config.proto +++ b/proxy/vmess/inbound/config.proto @@ -13,6 +13,7 @@ message DetourConfig { } message DefaultConfig { + uint32 alter_id = 1; uint32 level = 2; } @@ -20,5 +21,5 @@ message Config { repeated xray.common.protocol.User user = 1; DefaultConfig default = 2; DetourConfig detour = 3; - // 4 is for legacy setting + bool secure_encryption_only = 4; } diff --git a/proxy/vmess/inbound/errors.generated.go b/proxy/vmess/inbound/errors.generated.go new file mode 100644 index 00000000..c2d7295e --- /dev/null +++ b/proxy/vmess/inbound/errors.generated.go @@ -0,0 +1,9 @@ +package inbound + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 28b560d8..00b07f14 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -1,5 +1,7 @@ package inbound +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "io" @@ -12,6 +14,7 @@ import ( "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/platform" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/signal" @@ -26,16 +29,23 @@ import ( "github.com/xtls/xray-core/transport/internet/stat" ) +var ( + aeadForced = false + aeadForced2022 = false +) + type userByEmail struct { sync.Mutex - cache map[string]*protocol.MemoryUser - defaultLevel uint32 + cache map[string]*protocol.MemoryUser + defaultLevel uint32 + defaultAlterIDs uint16 } func newUserByEmail(config *DefaultConfig) *userByEmail { return &userByEmail{ - cache: make(map[string]*protocol.MemoryUser), - defaultLevel: config.Level, + cache: make(map[string]*protocol.MemoryUser), + defaultLevel: config.Level, + defaultAlterIDs: uint16(config.AlterId), } } @@ -56,7 +66,7 @@ func (v *userByEmail) Add(u *protocol.MemoryUser) bool { return v.addNoLock(u) } -func (v *userByEmail) GetOrGenerate(email string) (*protocol.MemoryUser, bool) { +func (v *userByEmail) Get(email string) (*protocol.MemoryUser, bool) { email = strings.ToLower(email) v.Lock() @@ -66,7 +76,8 @@ func (v *userByEmail) GetOrGenerate(email string) (*protocol.MemoryUser, bool) { if !found { id := uuid.New() rawAccount := &vmess.Account{ - Id: id.String(), + Id: id.String(), + AlterId: uint32(v.defaultAlterIDs), } account, err := rawAccount.AsAccount() common.Must(err) @@ -80,13 +91,6 @@ func (v *userByEmail) GetOrGenerate(email string) (*protocol.MemoryUser, bool) { return user, found } -func (v *userByEmail) Get(email string) *protocol.MemoryUser { - email = strings.ToLower(email) - v.Lock() - defer v.Unlock() - return v.cache[email] -} - func (v *userByEmail) Remove(email string) bool { email = strings.ToLower(email) @@ -108,6 +112,7 @@ type Handler struct { usersByEmail *userByEmail detours *DetourConfig sessionHistory *encoding.SessionHistory + secure bool } // New creates a new VMess inbound handler. @@ -116,20 +121,21 @@ func New(ctx context.Context, config *Config) (*Handler, error) { handler := &Handler{ policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), - clients: vmess.NewTimedUserValidator(), + clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash), detours: config.Detour, usersByEmail: newUserByEmail(config.GetDefaultValue()), sessionHistory: encoding.NewSessionHistory(), + secure: config.SecureEncryptionOnly, } for _, user := range config.User { mUser, err := user.ToMemoryUser() if err != nil { - return nil, errors.New("failed to get VMess user").Base(err) + return nil, newError("failed to get VMess user").Base(err) } if err := handler.AddUser(ctx, mUser); err != nil { - return nil, errors.New("failed to initiate user").Base(err) + return nil, newError("failed to initiate user").Base(err) } } @@ -139,6 +145,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { // Close implements common.Closable. func (h *Handler) Close() error { return errors.Combine( + h.clients.Close(), h.sessionHistory.Close(), common.Close(h.usersByEmail)) } @@ -148,39 +155,27 @@ func (*Handler) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} } -func (h *Handler) GetOrGenerateUser(email string) *protocol.MemoryUser { - user, existing := h.usersByEmail.GetOrGenerate(email) +func (h *Handler) GetUser(email string) *protocol.MemoryUser { + user, existing := h.usersByEmail.Get(email) if !existing { h.clients.Add(user) } return user } -func (h *Handler) GetUser(ctx context.Context, email string) *protocol.MemoryUser { - return h.usersByEmail.Get(email) -} - -func (h *Handler) GetUsers(ctx context.Context) []*protocol.MemoryUser { - return h.clients.GetUsers() -} - -func (h *Handler) GetUsersCount(context.Context) int64 { - return h.clients.GetCount() -} - func (h *Handler) AddUser(ctx context.Context, user *protocol.MemoryUser) error { if len(user.Email) > 0 && !h.usersByEmail.Add(user) { - return errors.New("User ", user.Email, " already exists.") + return newError("User ", user.Email, " already exists.") } return h.clients.Add(user) } func (h *Handler) RemoveUser(ctx context.Context, email string) error { if email == "" { - return errors.New("Email must not be empty.") + return newError("Email must not be empty.") } if !h.usersByEmail.Remove(email) { - return errors.New("User ", email, " not found.") + return newError("User ", email, " not found.") } h.clients.Remove(email) return nil @@ -191,7 +186,7 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess bodyWriter, err := session.EncodeResponseBody(request, output) if err != nil { - return errors.New("failed to start decoding response").Base(err) + return newError("failed to start decoding response").Base(err) } { // Optimize for small response packet @@ -224,11 +219,15 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess return nil } +func isInsecureEncryption(s protocol.SecurityType) bool { + return s == protocol.SecurityType_NONE || s == protocol.SecurityType_LEGACY || s == protocol.SecurityType_UNKNOWN +} + // Process implements proxy.Inbound.Process(). func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { sessionPolicy := h.policyManager.ForLevel(0) if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { - return errors.New("unable to set read deadline").Base(err).AtWarning() + return newError("unable to set read deadline").Base(err).AtWarning() } iConn := connection @@ -242,6 +241,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s reader := &buf.BufferedReader{Reader: buf.NewReader(connection)} svrSession := encoding.NewServerSession(h.clients, h.sessionHistory) + svrSession.SetAEADForced(aeadForced) request, err := svrSession.DecodeRequestHeader(reader, isDrain) if err != nil { if errors.Cause(err) != io.EOF { @@ -251,11 +251,22 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s Status: log.AccessRejected, Reason: err, }) - err = errors.New("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() + err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() } return err } + if h.secure && isInsecureEncryption(request.Security) { + log.Record(&log.AccessMessage{ + From: connection.RemoteAddr(), + To: "", + Status: log.AccessRejected, + Reason: "Insecure encryption", + Email: request.User.Email, + }) + return newError("client is using insecure encryption: ", request.Security) + } + if request.Command != protocol.RequestCommandMux { ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: connection.RemoteAddr(), @@ -266,15 +277,16 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s }) } - errors.LogInfo(ctx, "received request for ", request.Destination()) + newError("received request for ", request.Destination()).WriteToLog(session.ExportIDToError(ctx)) if err := connection.SetReadDeadline(time.Time{}); err != nil { - errors.LogInfoInner(ctx, err, "unable to set back read deadline") + newError("unable to set back read deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } inbound := session.InboundFromContext(ctx) - inbound.Name = "vmess" - inbound.CanSpliceCopy = 3 + if inbound == nil { + panic("no inbound metadata") + } inbound.User = request.User sessionPolicy = h.policyManager.ForLevel(request.User.Level) @@ -285,7 +297,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) link, err := dispatcher.Dispatch(ctx, request.Destination()) if err != nil { - return errors.New("failed to dispatch request to ", request.Destination()).Base(err) + return newError("failed to dispatch request to ", request.Destination()).Base(err) } requestDone := func() error { @@ -293,10 +305,10 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s bodyReader, err := svrSession.DecodeRequestBody(request, reader) if err != nil { - return errors.New("failed to start decoding").Base(err) + return newError("failed to start decoding").Base(err) } if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transfer request").Base(err) + return newError("failed to transfer request").Base(err) } return nil } @@ -317,7 +329,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil @@ -329,7 +341,7 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request if h.inboundHandlerManager != nil { handler, err := h.inboundHandlerManager.GetHandler(ctx, tag) if err != nil { - errors.LogWarningInner(ctx, err, "failed to get detour handler: ", tag) + newError("failed to get detour handler: ", tag).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) return nil } proxyHandler, port, availableMin := handler.GetRandomInboundProxy() @@ -339,8 +351,8 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request availableMin = 255 } - errors.LogDebug(ctx, "pick detour handler for port ", port, " for ", availableMin, " minutes.") - user := inboundHandler.GetOrGenerateUser(request.User.Email) + newError("pick detour handler for port ", port, " for ", availableMin, " minutes.").AtDebug().WriteToLog(session.ExportIDToError(ctx)) + user := inboundHandler.GetUser(request.User.Email) if user == nil { return nil } @@ -348,6 +360,7 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request return &protocol.CommandSwitchAccount{ Port: port, ID: account.ID.UUID(), + AlterIds: uint16(len(account.AlterIDs)), Level: user.Level, ValidMin: byte(availableMin), } @@ -362,4 +375,18 @@ func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) + + defaultFlagValue := "NOT_DEFINED_AT_ALL" + + if time.Now().Year() >= 2022 { + defaultFlagValue = "true_by_default_2022" + } + + isAeadForced := platform.NewEnvFlag("xray.vmess.aead.forced").GetValue(func() string { return defaultFlagValue }) + aeadForced = (isAeadForced == "true") + + if isAeadForced == "true_by_default_2022" { + aeadForced = true + aeadForced2022 = true + } } diff --git a/proxy/vmess/outbound/command.go b/proxy/vmess/outbound/command.go index 2d4747dc..00c6fac5 100644 --- a/proxy/vmess/outbound/command.go +++ b/proxy/vmess/outbound/command.go @@ -11,9 +11,10 @@ import ( func (h *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) { rawAccount := &vmess.Account{ - Id: cmd.ID.String(), + Id: cmd.ID.String(), + AlterId: uint32(cmd.AlterIds), SecuritySettings: &protocol.SecurityConfig{ - Type: protocol.SecurityType_AUTO, + Type: protocol.SecurityType_LEGACY, }, } diff --git a/proxy/vmess/outbound/config.pb.go b/proxy/vmess/outbound/config.pb.go index 0bc4d925..700127a8 100644 --- a/proxy/vmess/outbound/config.pb.go +++ b/proxy/vmess/outbound/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/vmess/outbound/config.proto package outbound @@ -31,9 +31,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_proxy_vmess_outbound_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_vmess_outbound_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -44,7 +46,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_outbound_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -103,7 +105,7 @@ func file_proxy_vmess_outbound_config_proto_rawDescGZIP() []byte { } var file_proxy_vmess_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_proxy_vmess_outbound_config_proto_goTypes = []any{ +var file_proxy_vmess_outbound_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.proxy.vmess.outbound.Config (*protocol.ServerEndpoint)(nil), // 1: xray.common.protocol.ServerEndpoint } @@ -121,6 +123,20 @@ func file_proxy_vmess_outbound_config_proto_init() { if File_proxy_vmess_outbound_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_vmess_outbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proxy/vmess/outbound/errors.generated.go b/proxy/vmess/outbound/errors.generated.go new file mode 100644 index 00000000..07966823 --- /dev/null +++ b/proxy/vmess/outbound/errors.generated.go @@ -0,0 +1,9 @@ +package outbound + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 27079e03..e7c6466e 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -1,5 +1,7 @@ package outbound +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "context" "crypto/hmac" @@ -9,7 +11,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/protocol" @@ -41,7 +42,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { for _, rec := range config.Receiver { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { - return nil, errors.New("failed to parse server spec").Base(err) + return nil, newError("failed to parse server spec").Base(err) } serverList.AddServer(s) } @@ -59,16 +60,9 @@ func New(ctx context.Context, config *Config) (*Handler, error) { // Process implements proxy.Outbound.Process(). func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified").AtError() - } - ob.Name = "vmess" - ob.CanSpliceCopy = 3 - var rec *protocol.ServerSpec var conn stat.Connection + err := retry.ExponentialBackoff(5, 200).On(func() error { rec = h.serverPicker.PickServer() rawConn, err := dialer.Dial(ctx, rec.Destination()) @@ -80,12 +74,17 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte return nil }) if err != nil { - return errors.New("failed to find an available destination").Base(err).AtWarning() + return newError("failed to find an available destination").Base(err).AtWarning() } defer conn.Close() - target := ob.Target - errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr()) + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified").AtError() + } + + target := outbound.Target + newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx)) command := protocol.RequestCommandTCP if target.Network == net.Network_UDP { @@ -129,27 +128,21 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte input := link.Reader output := link.Writer + isAEAD := false + if !aeadDisabled && len(account.AlterIDs) == 0 { + isAEAD = true + } + hashkdf := hmac.New(sha256.New, []byte("VMessBF")) hashkdf.Write(account.ID.Bytes()) behaviorSeed := crc64.Checksum(hashkdf.Sum(nil), crc64.MakeTable(crc64.ISO)) - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) - } - - session := encoding.NewClientSession(ctx, int64(behaviorSeed)) + session := encoding.NewClientSession(ctx, isAEAD, protocol.DefaultIDHash, int64(behaviorSeed)) sessionPolicy := h.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, sessionPolicy.Timeouts.ConnectionIdle) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 { request.Command = protocol.RequestCommandMux @@ -162,19 +155,19 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte writer := buf.NewBufferedWriter(buf.NewWriter(conn)) if err := session.EncodeRequestHeader(request, writer); err != nil { - return errors.New("failed to encode request").Base(err).AtWarning() + return newError("failed to encode request").Base(err).AtWarning() } bodyWriter, err := session.EncodeRequestBody(request, writer) if err != nil { - return errors.New("failed to start encoding").Base(err) + return newError("failed to start encoding").Base(err) } bodyWriter2 := bodyWriter if request.Command == protocol.RequestCommandMux && request.Port == 666 { - bodyWriter = xudp.NewPacketWriter(bodyWriter, target, xudp.GetGlobalID(ctx)) + bodyWriter = xudp.NewPacketWriter(bodyWriter, target) } if err := buf.CopyOnceTimeout(input, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { - return errors.New("failed to write first payload").Base(err) + return newError("failed to write first payload").Base(err) } if err := writer.SetBuffered(false); err != nil { @@ -200,13 +193,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} header, err := session.DecodeResponseHeader(reader) if err != nil { - return errors.New("failed to read header").Base(err) + return newError("failed to read header").Base(err) } h.handleCommand(rec.Destination(), header.Command) bodyReader, err := session.DecodeResponseBody(request, reader) if err != nil { - return errors.New("failed to start encoding response").Base(err) + return newError("failed to start encoding response").Base(err) } if request.Command == protocol.RequestCommandMux && request.Port == 666 { bodyReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: bodyReader}) @@ -215,13 +208,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte return buf.Copy(bodyReader, output, buf.UpdateActivity(timer)) } - if newCtx != nil { - ctx = newCtx - } - responseDonePost := task.OnSuccess(responseDone, task.Close(output)) if err := task.Run(ctx, requestDone, responseDonePost); err != nil { - return errors.New("connection ends").Base(err) + return newError("connection ends").Base(err) } return nil @@ -229,6 +218,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte var ( enablePadding = false + aeadDisabled = false ) func shouldEnablePadding(s protocol.SecurityType) bool { @@ -242,8 +232,13 @@ func init() { const defaultFlagValue = "NOT_DEFINED_AT_ALL" - paddingValue := platform.NewEnvFlag(platform.UseVmessPadding).GetValue(func() string { return defaultFlagValue }) + paddingValue := platform.NewEnvFlag("xray.vmess.padding").GetValue(func() string { return defaultFlagValue }) if paddingValue != defaultFlagValue { enablePadding = true } + + isAeadDisabled := platform.NewEnvFlag("xray.vmess.aead.disabled").GetValue(func() string { return defaultFlagValue }) + if isAeadDisabled == "true" { + aeadDisabled = true + } } diff --git a/proxy/vmess/validator.go b/proxy/vmess/validator.go index bfffadcf..c638a23b 100644 --- a/proxy/vmess/validator.go +++ b/proxy/vmess/validator.go @@ -6,43 +6,146 @@ import ( "hash/crc64" "strings" "sync" + "sync/atomic" + "time" + "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/protocol" + "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/proxy/vmess/aead" ) +const ( + updateInterval = 10 * time.Second + cacheDurationSec = 120 +) + +type user struct { + user protocol.MemoryUser + lastSec protocol.Timestamp +} + // TimedUserValidator is a user Validator based on time. type TimedUserValidator struct { sync.RWMutex - users []*protocol.MemoryUser + users []*user + userHash map[[16]byte]indexTimePair + hasher protocol.IDHash + baseTime protocol.Timestamp + task *task.Periodic behaviorSeed uint64 behaviorFused bool aeadDecoderHolder *aead.AuthIDDecoderHolder + + legacyWarnShown bool +} + +type indexTimePair struct { + user *user + timeInc uint32 + + taintedFuse *uint32 } // NewTimedUserValidator creates a new TimedUserValidator. -func NewTimedUserValidator() *TimedUserValidator { +func NewTimedUserValidator(hasher protocol.IDHash) *TimedUserValidator { tuv := &TimedUserValidator{ - users: make([]*protocol.MemoryUser, 0, 16), + users: make([]*user, 0, 16), + userHash: make(map[[16]byte]indexTimePair, 1024), + hasher: hasher, + baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*2), aeadDecoderHolder: aead.NewAuthIDDecoderHolder(), } + tuv.task = &task.Periodic{ + Interval: updateInterval, + Execute: func() error { + tuv.updateUserHash() + return nil + }, + } + common.Must(tuv.task.Start()) return tuv } +// visible for testing +func (v *TimedUserValidator) GetBaseTime() protocol.Timestamp { + return v.baseTime +} + +func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) { + var hashValue [16]byte + genEndSec := nowSec + cacheDurationSec + genHashForID := func(id *protocol.ID) { + idHash := v.hasher(id.Bytes()) + genBeginSec := user.lastSec + if genBeginSec < nowSec-cacheDurationSec { + genBeginSec = nowSec - cacheDurationSec + } + for ts := genBeginSec; ts <= genEndSec; ts++ { + common.Must2(serial.WriteUint64(idHash, uint64(ts))) + idHash.Sum(hashValue[:0]) + idHash.Reset() + + v.userHash[hashValue] = indexTimePair{ + user: user, + timeInc: uint32(ts - v.baseTime), + taintedFuse: new(uint32), + } + } + } + + account := user.user.Account.(*MemoryAccount) + + genHashForID(account.ID) + for _, id := range account.AlterIDs { + genHashForID(id) + } + user.lastSec = genEndSec +} + +func (v *TimedUserValidator) removeExpiredHashes(expire uint32) { + for key, pair := range v.userHash { + if pair.timeInc < expire { + delete(v.userHash, key) + } + } +} + +func (v *TimedUserValidator) updateUserHash() { + now := time.Now() + nowSec := protocol.Timestamp(now.Unix()) + + v.Lock() + defer v.Unlock() + + for _, user := range v.users { + v.generateNewHashes(nowSec, user) + } + + expire := protocol.Timestamp(now.Unix() - cacheDurationSec) + if expire > v.baseTime { + v.removeExpiredHashes(uint32(expire - v.baseTime)) + } +} + func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error { v.Lock() defer v.Unlock() - v.users = append(v.users, u) + nowSec := time.Now().Unix() - account, ok := u.Account.(*MemoryAccount) - if !ok { - return errors.New("account type is incorrect") + uu := &user{ + user: *u, + lastSec: protocol.Timestamp(nowSec - cacheDurationSec), } + v.users = append(v.users, uu) + v.generateNewHashes(protocol.Timestamp(nowSec), uu) + + account := uu.user.Account.(*MemoryAccount) if !v.behaviorFused { hashkdf := hmac.New(sha256.New, []byte("VMESSBSKDF")) hashkdf.Write(account.ID.Bytes()) @@ -56,18 +159,23 @@ func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error { return nil } -func (v *TimedUserValidator) GetUsers() []*protocol.MemoryUser { - v.Lock() - defer v.Unlock() - dst := make([]*protocol.MemoryUser, len(v.users)) - copy(dst, v.users) - return dst -} +func (v *TimedUserValidator) Get(userHash []byte) (*protocol.MemoryUser, protocol.Timestamp, bool, error) { + v.RLock() + defer v.RUnlock() -func (v *TimedUserValidator) GetCount() int64 { - v.Lock() - defer v.Unlock() - return int64(len(v.users)) + v.behaviorFused = true + + var fixedSizeHash [16]byte + copy(fixedSizeHash[:], userHash) + pair, found := v.userHash[fixedSizeHash] + if found { + user := pair.user.user + if atomic.LoadUint32(pair.taintedFuse) == 0 { + return &user, protocol.Timestamp(pair.timeInc) + v.baseTime, true, nil + } + return nil, 0, false, ErrTainted + } + return nil, 0, false, ErrNotFound } func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, bool, error) { @@ -81,7 +189,7 @@ func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, boo if err != nil { return nil, false, err } - return userd.(*protocol.MemoryUser), true, nil + return userd.(*protocol.MemoryUser), true, err } func (v *TimedUserValidator) Remove(email string) bool { @@ -91,10 +199,10 @@ func (v *TimedUserValidator) Remove(email string) bool { email = strings.ToLower(email) idx := -1 for i, u := range v.users { - if strings.EqualFold(u.Email, email) { + if strings.EqualFold(u.user.Email, email) { idx = i var cmdkeyfl [16]byte - copy(cmdkeyfl[:], u.Account.(*MemoryAccount).ID.CmdKey()) + copy(cmdkeyfl[:], u.user.Account.(*MemoryAccount).ID.CmdKey()) v.aeadDecoderHolder.RemoveUser(cmdkeyfl) break } @@ -111,6 +219,11 @@ func (v *TimedUserValidator) Remove(email string) bool { return true } +// Close implements common.Closable. +func (v *TimedUserValidator) Close() error { + return v.task.Close() +} + func (v *TimedUserValidator) GetBehaviorSeed() uint64 { v.Lock() defer v.Unlock() @@ -122,6 +235,36 @@ func (v *TimedUserValidator) GetBehaviorSeed() uint64 { return v.behaviorSeed } -var ErrNotFound = errors.New("Not Found") +func (v *TimedUserValidator) BurnTaintFuse(userHash []byte) error { + v.RLock() + defer v.RUnlock() -var ErrTainted = errors.New("ErrTainted") + var userHashFL [16]byte + copy(userHashFL[:], userHash) + + pair, found := v.userHash[userHashFL] + if found { + if atomic.CompareAndSwapUint32(pair.taintedFuse, 0, 1) { + return nil + } + return ErrTainted + } + return ErrNotFound +} + +/* + ShouldShowLegacyWarn will return whether a Legacy Warning should be shown + +Not guaranteed to only return true once for every inbound, but it is okay. +*/ +func (v *TimedUserValidator) ShouldShowLegacyWarn() bool { + if v.legacyWarnShown { + return false + } + v.legacyWarnShown = true + return true +} + +var ErrNotFound = newError("Not Found") + +var ErrTainted = newError("ErrTainted") diff --git a/proxy/vmess/validator_test.go b/proxy/vmess/validator_test.go index 83313cbc..ee170655 100644 --- a/proxy/vmess/validator_test.go +++ b/proxy/vmess/validator_test.go @@ -5,6 +5,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/uuid" . "github.com/xtls/xray-core/proxy/vmess" ) @@ -15,9 +16,81 @@ func toAccount(a *Account) protocol.Account { return account } +func TestUserValidator(t *testing.T) { + hasher := protocol.DefaultIDHash + v := NewTimedUserValidator(hasher) + defer common.Close(v) + + id := uuid.New() + user := &protocol.MemoryUser{ + Email: "test", + Account: toAccount(&Account{ + Id: id.String(), + }), + } + common.Must(v.Add(user)) + + { + testSmallLag := func(lag int64) { + ts := int64(v.GetBaseTime()) + lag + 240 + idHash := hasher(id.Bytes()) + common.Must2(serial.WriteUint64(idHash, uint64(ts))) + userHash := idHash.Sum(nil) + + euser, ets, found, _ := v.Get(userHash) + if !found { + t.Fatal("user not found") + } + if euser.Email != user.Email { + t.Error("unexpected user email: ", euser.Email, " want ", user.Email) + } + if int64(ets) != ts { + t.Error("unexpected timestamp: ", ets, " want ", ts) + } + } + + testSmallLag(0) + testSmallLag(40) + testSmallLag(-40) + testSmallLag(80) + testSmallLag(-80) + testSmallLag(120) + testSmallLag(-120) + } + + { + testBigLag := func(lag int64) { + ts := int64(v.GetBaseTime()) + lag + 240 + idHash := hasher(id.Bytes()) + common.Must2(serial.WriteUint64(idHash, uint64(ts))) + userHash := idHash.Sum(nil) + + euser, _, found, _ := v.Get(userHash) + if found || euser != nil { + t.Error("unexpected user") + } + } + + testBigLag(121) + testBigLag(-121) + testBigLag(310) + testBigLag(-310) + testBigLag(500) + testBigLag(-500) + } + + if v := v.Remove(user.Email); !v { + t.Error("unable to remove user") + } + if v := v.Remove(user.Email); v { + t.Error("remove user twice") + } +} + func BenchmarkUserValidator(b *testing.B) { for i := 0; i < b.N; i++ { - v := NewTimedUserValidator() + hasher := protocol.DefaultIDHash + v := NewTimedUserValidator(hasher) for j := 0; j < 1500; j++ { id := uuid.New() diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index dd86f516..836b1889 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -4,3 +4,5 @@ // together with 'freedom' to talk to final destination, while VMess outbound is usually used on // clients with 'socks' for proxying. package vmess + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/proxy/vmess/vmessCtxInterface.go b/proxy/vmess/vmessCtxInterface.go new file mode 100644 index 00000000..5d26f9e5 --- /dev/null +++ b/proxy/vmess/vmessCtxInterface.go @@ -0,0 +1,4 @@ +package vmess + +// example +const AlterID = "VMessCtxInterface_AlterID" diff --git a/proxy/wireguard/bind.go b/proxy/wireguard/bind.go index 79de7eba..527f0e74 100644 --- a/proxy/wireguard/bind.go +++ b/proxy/wireguard/bind.go @@ -9,8 +9,7 @@ import ( "strconv" "sync" - "golang.zx2c4.com/wireguard/conn" - + "github.com/sagernet/wireguard-go/conn" xnet "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/transport/internet" @@ -27,45 +26,48 @@ type netReadInfo struct { err error } -// reduce duplicated code -type netBind struct { +type netBindClient struct { + workers int + dialer internet.Dialer dns dns.Client dnsOption dns.IPOption + reserved []byte - workers int readQueue chan *netReadInfo } -// SetMark implements conn.Bind -func (bind *netBind) SetMark(mark uint32) error { - return nil -} - -// ParseEndpoint implements conn.Bind -func (n *netBind) ParseEndpoint(s string) (conn.Endpoint, error) { - ipStr, port, err := net.SplitHostPort(s) - if err != nil { - return nil, err - } - portNum, err := strconv.Atoi(port) +func (n *netBindClient) ParseEndpoint(s string) (conn.Endpoint, error) { + ipStr, port, _, err := splitAddrPort(s) if err != nil { return nil, err } - addr := xnet.ParseAddress(ipStr) - if addr.Family() == xnet.AddressFamilyDomain { - ips, _, err := n.dns.LookupIP(addr.Domain(), n.dnsOption) + var addr net.IP + if IsDomainName(ipStr) { + ips, err := n.dns.LookupIP(ipStr, n.dnsOption) if err != nil { return nil, err } else if len(ips) == 0 { return nil, dns.ErrEmptyResponse } - addr = xnet.IPAddress(ips[0]) + addr = ips[0] + } else { + addr = net.ParseIP(ipStr) + } + if addr == nil { + return nil, errors.New("failed to parse ip: " + ipStr) + } + + var ip xnet.Address + if p4 := addr.To4(); len(p4) == net.IPv4len { + ip = xnet.IPAddress(p4[:]) + } else { + ip = xnet.IPAddress(addr[:]) } dst := xnet.Destination{ - Address: addr, - Port: xnet.Port(portNum), + Address: ip, + Port: xnet.Port(port), Network: xnet.Network_UDP, } @@ -74,31 +76,25 @@ func (n *netBind) ParseEndpoint(s string) (conn.Endpoint, error) { }, nil } -// BatchSize implements conn.Bind -func (bind *netBind) BatchSize() int { - return 1 -} - -// Open implements conn.Bind -func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { +func (bind *netBindClient) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { bind.readQueue = make(chan *netReadInfo) - fun := func(bufs [][]byte, sizes []int, eps []conn.Endpoint) (n int, err error) { + fun := func(buff []byte) (cap int, ep conn.Endpoint, err error) { defer func() { if r := recover(); r != nil { - n = 0 + cap = 0 + ep = nil err = errors.New("channel closed") } }() r := &netReadInfo{ - buff: bufs[0], + buff: buff, } r.waiter.Add(1) bind.readQueue <- r r.waiter.Wait() // wait read goroutine done, or we will miss the result - sizes[0], eps[0] = r.bytes, r.endpoint - return 1, r.err + return r.bytes, r.endpoint, r.err } workers := bind.workers if workers <= 0 { @@ -112,24 +108,15 @@ func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { return arr, uint16(uport), nil } -// Close implements conn.Bind -func (bind *netBind) Close() error { +func (bind *netBindClient) Close() error { if bind.readQueue != nil { close(bind.readQueue) } return nil } -type netBindClient struct { - netBind - - ctx context.Context - dialer internet.Dialer - reserved []byte -} - func (bind *netBindClient) connectTo(endpoint *netEndpoint) error { - c, err := bind.dialer.Dial(bind.ctx, endpoint.dst) + c, err := bind.dialer.Dial(context.Background(), endpoint.dst) if err != nil { return err } @@ -163,7 +150,7 @@ func (bind *netBindClient) connectTo(endpoint *netEndpoint) error { return nil } -func (bind *netBindClient) Send(buff [][]byte, endpoint conn.Endpoint) error { +func (bind *netBindClient) Send(buff []byte, endpoint conn.Endpoint) error { var err error nend, ok := endpoint.(*netEndpoint) @@ -178,42 +165,19 @@ func (bind *netBindClient) Send(buff [][]byte, endpoint conn.Endpoint) error { } } - for _, buff := range buff { - if len(buff) > 3 && len(bind.reserved) == 3 { - copy(buff[1:], bind.reserved) - } - if _, err = nend.conn.Write(buff); err != nil { - return err - } - } - return nil -} - -type netBindServer struct { - netBind -} - -func (bind *netBindServer) Send(buff [][]byte, endpoint conn.Endpoint) error { - var err error - - nend, ok := endpoint.(*netEndpoint) - if !ok { - return conn.ErrWrongEndpointType + if len(buff) > 3 && len(bind.reserved) == 3 { + copy(buff[1:], bind.reserved) } - if nend.conn == nil { - return errors.New("connection not open yet") - } - - for _, buff := range buff { - if _, err = nend.conn.Write(buff); err != nil { - return err - } - } + _, err = nend.conn.Write(buff) return err } +func (bind *netBindClient) SetMark(mark uint32) error { + return nil +} + type netEndpoint struct { dst xnet.Destination conn net.Conn @@ -222,7 +186,7 @@ type netEndpoint struct { func (netEndpoint) ClearSrc() {} func (e netEndpoint) DstIP() netip.Addr { - return netip.Addr{} + return toNetIpAddr(e.dst.Address) } func (e netEndpoint) SrcIP() netip.Addr { @@ -261,3 +225,42 @@ func toNetIpAddr(addr xnet.Address) netip.Addr { return netip.AddrFrom16(arr) } } + +func stringsLastIndexByte(s string, b byte) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == b { + return i + } + } + return -1 +} + +func splitAddrPort(s string) (ip string, port uint16, v6 bool, err error) { + i := stringsLastIndexByte(s, ':') + if i == -1 { + return "", 0, false, errors.New("not an ip:port") + } + + ip = s[:i] + portStr := s[i+1:] + if len(ip) == 0 { + return "", 0, false, errors.New("no IP") + } + if len(portStr) == 0 { + return "", 0, false, errors.New("no port") + } + port64, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return "", 0, false, errors.New("invalid port " + strconv.Quote(portStr) + " parsing " + strconv.Quote(s)) + } + port = uint16(port64) + if ip[0] == '[' { + if len(ip) < 2 || ip[len(ip)-1] != ']' { + return "", 0, false, errors.New("missing ]") + } + ip = ip[1 : len(ip)-1] + v6 = true + } + + return ip, port, v6, nil +} diff --git a/proxy/wireguard/client.go b/proxy/wireguard/client.go deleted file mode 100644 index 345581c7..00000000 --- a/proxy/wireguard/client.go +++ /dev/null @@ -1,337 +0,0 @@ -/* - -Some of codes are copied from https://github.com/octeep/wireproxy, license below. - -Copyright (c) 2022 Wind T.F. Wong - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -*/ - -package wireguard - -import ( - "context" - "fmt" - "net/netip" - "strings" - "sync" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/dice" - "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/protocol" - "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/core" - "github.com/xtls/xray-core/features/dns" - "github.com/xtls/xray-core/features/policy" - "github.com/xtls/xray-core/transport" - "github.com/xtls/xray-core/transport/internet" -) - -// Handler is an outbound connection that silently swallow the entire payload. -type Handler struct { - conf *DeviceConfig - net Tunnel - bind *netBindClient - policyManager policy.Manager - dns dns.Client - // cached configuration - endpoints []netip.Addr - hasIPv4, hasIPv6 bool - wgLock sync.Mutex -} - -// New creates a new wireguard handler. -func New(ctx context.Context, conf *DeviceConfig) (*Handler, error) { - v := core.MustFromContext(ctx) - - endpoints, hasIPv4, hasIPv6, err := parseEndpoints(conf) - if err != nil { - return nil, err - } - - d := v.GetFeature(dns.ClientType()).(dns.Client) - return &Handler{ - conf: conf, - policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), - dns: d, - endpoints: endpoints, - hasIPv4: hasIPv4, - hasIPv6: hasIPv6, - }, nil -} - -func (h *Handler) Close() (err error) { - go func() { - h.wgLock.Lock() - defer h.wgLock.Unlock() - - if h.net != nil { - _ = h.net.Close() - h.net = nil - } - }() - - return nil -} - -func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer) (err error) { - h.wgLock.Lock() - defer h.wgLock.Unlock() - - if h.bind != nil && h.bind.dialer == dialer && h.net != nil { - return nil - } - - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Info, - Content: "switching dialer", - }) - - if h.net != nil { - _ = h.net.Close() - h.net = nil - } - if h.bind != nil { - _ = h.bind.Close() - h.bind = nil - } - - // bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer - h.bind = &netBindClient{ - netBind: netBind{ - dns: h.dns, - dnsOption: dns.IPOption{ - IPv4Enable: h.hasIPv4, - IPv6Enable: h.hasIPv6, - }, - workers: int(h.conf.NumWorkers), - }, - ctx: ctx, - dialer: dialer, - reserved: h.conf.Reserved, - } - defer func() { - if err != nil { - _ = h.bind.Close() - } - }() - - h.net, err = h.makeVirtualTun() - if err != nil { - return errors.New("failed to create virtual tun interface").Base(err) - } - return nil -} - -// Process implements OutboundHandler.Dispatch(). -func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - if !ob.Target.IsValid() { - return errors.New("target not specified") - } - ob.Name = "wireguard" - ob.CanSpliceCopy = 3 - - if err := h.processWireGuard(ctx, dialer); err != nil { - return err - } - - // Destination of the inner request. - destination := ob.Target - command := protocol.RequestCommandTCP - if destination.Network == net.Network_UDP { - command = protocol.RequestCommandUDP - } - - // resolve dns - addr := destination.Address - if addr.Family().IsDomain() { - ips, _, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ - IPv4Enable: h.hasIPv4 && h.conf.preferIP4(), - IPv6Enable: h.hasIPv6 && h.conf.preferIP6(), - }) - { // Resolve fallback - if (len(ips) == 0 || err != nil) && h.conf.hasFallback() { - ips, _, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ - IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(), - IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(), - }) - } - } - if err != nil { - return errors.New("failed to lookup DNS").Base(err) - } else if len(ips) == 0 { - return dns.ErrEmptyResponse - } - addr = net.IPAddress(ips[dice.Roll(len(ips))]) - } - - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) - } - - p := h.policyManager.ForLevel(0) - - ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, p.Timeouts.ConnectionIdle) - addrPort := netip.AddrPortFrom(toNetIpAddr(addr), destination.Port.Value()) - - var requestFunc func() error - var responseFunc func() error - - if command == protocol.RequestCommandTCP { - conn, err := h.net.DialContextTCPAddrPort(ctx, addrPort) - if err != nil { - return errors.New("failed to create TCP connection").Base(err) - } - defer conn.Close() - - requestFunc = func() error { - defer timer.SetTimeout(p.Timeouts.DownlinkOnly) - return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) - } - responseFunc = func() error { - defer timer.SetTimeout(p.Timeouts.UplinkOnly) - return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) - } - } else if command == protocol.RequestCommandUDP { - conn, err := h.net.DialUDPAddrPort(netip.AddrPort{}, addrPort) - if err != nil { - return errors.New("failed to create UDP connection").Base(err) - } - defer conn.Close() - - requestFunc = func() error { - defer timer.SetTimeout(p.Timeouts.DownlinkOnly) - return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) - } - responseFunc = func() error { - defer timer.SetTimeout(p.Timeouts.UplinkOnly) - return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) - } - } - - if newCtx != nil { - ctx = newCtx - } - - responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) - if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { - common.Interrupt(link.Reader) - common.Interrupt(link.Writer) - return errors.New("connection ends").Base(err) - } - - return nil -} - -// creates a tun interface on netstack given a configuration -func (h *Handler) makeVirtualTun() (Tunnel, error) { - t, err := h.conf.createTun()(h.endpoints, int(h.conf.Mtu), nil) - if err != nil { - return nil, err - } - - h.bind.dnsOption.IPv4Enable = h.hasIPv4 - h.bind.dnsOption.IPv6Enable = h.hasIPv6 - - if err = t.BuildDevice(h.createIPCRequest(), h.bind); err != nil { - _ = t.Close() - return nil, err - } - return t, nil -} - -// serialize the config into an IPC request -func (h *Handler) createIPCRequest() string { - var request strings.Builder - - request.WriteString(fmt.Sprintf("private_key=%s\n", h.conf.SecretKey)) - - if !h.conf.IsClient { - // placeholder, we'll handle actual port listening on Xray - request.WriteString("listen_port=1337\n") - } - - for _, peer := range h.conf.Peers { - if peer.PublicKey != "" { - request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey)) - } - - if peer.PreSharedKey != "" { - request.WriteString(fmt.Sprintf("preshared_key=%s\n", peer.PreSharedKey)) - } - - address, port, err := net.SplitHostPort(peer.Endpoint) - if err != nil { - errors.LogError(h.bind.ctx, "failed to split endpoint ", peer.Endpoint, " into address and port") - } - addr := net.ParseAddress(address) - if addr.Family().IsDomain() { - dialerIp := h.bind.dialer.DestIpAddress() - if dialerIp != nil { - addr = net.ParseAddress(dialerIp.String()) - errors.LogInfo(h.bind.ctx, "createIPCRequest use dialer dest ip: ", addr) - } else { - ips, _, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ - IPv4Enable: h.hasIPv4 && h.conf.preferIP4(), - IPv6Enable: h.hasIPv6 && h.conf.preferIP6(), - }) - { // Resolve fallback - if (len(ips) == 0 || err != nil) && h.conf.hasFallback() { - ips, _, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ - IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(), - IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(), - }) - } - } - if err != nil { - errors.LogInfoInner(h.bind.ctx, err, "createIPCRequest failed to lookup DNS") - } else if len(ips) == 0 { - errors.LogInfo(h.bind.ctx, "createIPCRequest empty lookup DNS") - } else { - addr = net.IPAddress(ips[dice.Roll(len(ips))]) - } - } - } - - if peer.Endpoint != "" { - request.WriteString(fmt.Sprintf("endpoint=%s:%s\n", addr, port)) - } - - for _, ip := range peer.AllowedIps { - request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip)) - } - - if peer.KeepAlive != 0 { - request.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", peer.KeepAlive)) - } - } - - return request.String()[:request.Len()] -} diff --git a/proxy/wireguard/config.go b/proxy/wireguard/config.go deleted file mode 100644 index cbaa670b..00000000 --- a/proxy/wireguard/config.go +++ /dev/null @@ -1,54 +0,0 @@ -package wireguard - -import ( - "context" - - "github.com/xtls/xray-core/common/errors" -) - -func (c *DeviceConfig) preferIP4() bool { - return c.DomainStrategy == DeviceConfig_FORCE_IP || - c.DomainStrategy == DeviceConfig_FORCE_IP4 || - c.DomainStrategy == DeviceConfig_FORCE_IP46 -} - -func (c *DeviceConfig) preferIP6() bool { - return c.DomainStrategy == DeviceConfig_FORCE_IP || - c.DomainStrategy == DeviceConfig_FORCE_IP6 || - c.DomainStrategy == DeviceConfig_FORCE_IP64 -} - -func (c *DeviceConfig) hasFallback() bool { - return c.DomainStrategy == DeviceConfig_FORCE_IP46 || c.DomainStrategy == DeviceConfig_FORCE_IP64 -} - -func (c *DeviceConfig) fallbackIP4() bool { - return c.DomainStrategy == DeviceConfig_FORCE_IP64 -} - -func (c *DeviceConfig) fallbackIP6() bool { - return c.DomainStrategy == DeviceConfig_FORCE_IP46 -} - -func (c *DeviceConfig) createTun() tunCreator { - if !c.IsClient { - // See tun_linux.go createKernelTun() - errors.LogWarning(context.Background(), "Using gVisor TUN. WG inbound doesn't support kernel TUN yet.") - return createGVisorTun - } - if c.NoKernelTun { - errors.LogWarning(context.Background(), "Using gVisor TUN. NoKernelTun is set to true.") - return createGVisorTun - } - kernelTunSupported, err := KernelTunSupported() - if err != nil { - errors.LogWarning(context.Background(), "Using gVisor TUN. Failed to check kernel TUN support:", err) - return createGVisorTun - } - if !kernelTunSupported { - errors.LogWarning(context.Background(), "Using gVisor TUN. Kernel TUN is not supported on your OS, or your permission is insufficient.") - return createGVisorTun - } - errors.LogWarning(context.Background(), "Using kernel TUN.") - return createKernelTun -} diff --git a/proxy/wireguard/config.pb.go b/proxy/wireguard/config.pb.go index 17f1a174..75d5787b 100644 --- a/proxy/wireguard/config.pb.go +++ b/proxy/wireguard/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: proxy/wireguard/config.proto package wireguard @@ -20,61 +20,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type DeviceConfig_DomainStrategy int32 - -const ( - DeviceConfig_FORCE_IP DeviceConfig_DomainStrategy = 0 - DeviceConfig_FORCE_IP4 DeviceConfig_DomainStrategy = 1 - DeviceConfig_FORCE_IP6 DeviceConfig_DomainStrategy = 2 - DeviceConfig_FORCE_IP46 DeviceConfig_DomainStrategy = 3 - DeviceConfig_FORCE_IP64 DeviceConfig_DomainStrategy = 4 -) - -// Enum value maps for DeviceConfig_DomainStrategy. -var ( - DeviceConfig_DomainStrategy_name = map[int32]string{ - 0: "FORCE_IP", - 1: "FORCE_IP4", - 2: "FORCE_IP6", - 3: "FORCE_IP46", - 4: "FORCE_IP64", - } - DeviceConfig_DomainStrategy_value = map[string]int32{ - "FORCE_IP": 0, - "FORCE_IP4": 1, - "FORCE_IP6": 2, - "FORCE_IP46": 3, - "FORCE_IP64": 4, - } -) - -func (x DeviceConfig_DomainStrategy) Enum() *DeviceConfig_DomainStrategy { - p := new(DeviceConfig_DomainStrategy) - *p = x - return p -} - -func (x DeviceConfig_DomainStrategy) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (DeviceConfig_DomainStrategy) Descriptor() protoreflect.EnumDescriptor { - return file_proxy_wireguard_config_proto_enumTypes[0].Descriptor() -} - -func (DeviceConfig_DomainStrategy) Type() protoreflect.EnumType { - return &file_proxy_wireguard_config_proto_enumTypes[0] -} - -func (x DeviceConfig_DomainStrategy) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use DeviceConfig_DomainStrategy.Descriptor instead. -func (DeviceConfig_DomainStrategy) EnumDescriptor() ([]byte, []int) { - return file_proxy_wireguard_config_proto_rawDescGZIP(), []int{1, 0} -} - type PeerConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -83,15 +28,17 @@ type PeerConfig struct { PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` PreSharedKey string `protobuf:"bytes,2,opt,name=pre_shared_key,json=preSharedKey,proto3" json:"pre_shared_key,omitempty"` Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - KeepAlive uint32 `protobuf:"varint,4,opt,name=keep_alive,json=keepAlive,proto3" json:"keep_alive,omitempty"` + KeepAlive int32 `protobuf:"varint,4,opt,name=keep_alive,json=keepAlive,proto3" json:"keep_alive,omitempty"` AllowedIps []string `protobuf:"bytes,5,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` } func (x *PeerConfig) Reset() { *x = PeerConfig{} - mi := &file_proxy_wireguard_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_wireguard_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *PeerConfig) String() string { @@ -102,7 +49,7 @@ func (*PeerConfig) ProtoMessage() {} func (x *PeerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_wireguard_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -138,7 +85,7 @@ func (x *PeerConfig) GetEndpoint() string { return "" } -func (x *PeerConfig) GetKeepAlive() uint32 { +func (x *PeerConfig) GetKeepAlive() int32 { if x != nil { return x.KeepAlive } @@ -157,22 +104,21 @@ type DeviceConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` - Endpoint []string `protobuf:"bytes,2,rep,name=endpoint,proto3" json:"endpoint,omitempty"` - Peers []*PeerConfig `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` - Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` - NumWorkers int32 `protobuf:"varint,5,opt,name=num_workers,json=numWorkers,proto3" json:"num_workers,omitempty"` - Reserved []byte `protobuf:"bytes,6,opt,name=reserved,proto3" json:"reserved,omitempty"` - DomainStrategy DeviceConfig_DomainStrategy `protobuf:"varint,7,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.wireguard.DeviceConfig_DomainStrategy" json:"domain_strategy,omitempty"` - IsClient bool `protobuf:"varint,8,opt,name=is_client,json=isClient,proto3" json:"is_client,omitempty"` - NoKernelTun bool `protobuf:"varint,9,opt,name=no_kernel_tun,json=noKernelTun,proto3" json:"no_kernel_tun,omitempty"` + SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` + Endpoint []string `protobuf:"bytes,2,rep,name=endpoint,proto3" json:"endpoint,omitempty"` + Peers []*PeerConfig `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` + Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` + NumWorkers int32 `protobuf:"varint,5,opt,name=num_workers,json=numWorkers,proto3" json:"num_workers,omitempty"` + Reserved []byte `protobuf:"bytes,6,opt,name=reserved,proto3" json:"reserved,omitempty"` } func (x *DeviceConfig) Reset() { *x = DeviceConfig{} - mi := &file_proxy_wireguard_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_proxy_wireguard_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DeviceConfig) String() string { @@ -183,7 +129,7 @@ func (*DeviceConfig) ProtoMessage() {} func (x *DeviceConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_wireguard_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -240,27 +186,6 @@ func (x *DeviceConfig) GetReserved() []byte { return nil } -func (x *DeviceConfig) GetDomainStrategy() DeviceConfig_DomainStrategy { - if x != nil { - return x.DomainStrategy - } - return DeviceConfig_FORCE_IP -} - -func (x *DeviceConfig) GetIsClient() bool { - if x != nil { - return x.IsClient - } - return false -} - -func (x *DeviceConfig) GetNoKernelTun() bool { - if x != nil { - return x.NoKernelTun - } - return false -} - var File_proxy_wireguard_config_proto protoreflect.FileDescriptor var file_proxy_wireguard_config_proto_rawDesc = []byte{ @@ -275,10 +200,10 @@ var file_proxy_wireguard_config_proto_rawDesc = []byte{ 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, - 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, + 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x49, 0x70, 0x73, 0x22, 0xcb, 0x03, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x64, 0x49, 0x70, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, @@ -291,29 +216,13 @@ var file_proxy_wireguard_config_proto_rawDesc = []byte{ 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x5a, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x31, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, - 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x74, 0x75, - 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x4b, 0x65, 0x72, 0x6e, 0x65, - 0x6c, 0x54, 0x75, 0x6e, 0x22, 0x5c, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, - 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, - 0x49, 0x50, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, - 0x34, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, - 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, - 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, - 0x10, 0x04, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, - 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 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, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x2f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0xaa, 0x02, 0x14, 0x58, 0x72, - 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, - 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, + 0x61, 0x72, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, + 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x57, 0x69, + 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -328,21 +237,18 @@ func file_proxy_wireguard_config_proto_rawDescGZIP() []byte { return file_proxy_wireguard_config_proto_rawDescData } -var file_proxy_wireguard_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_proxy_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_proxy_wireguard_config_proto_goTypes = []any{ - (DeviceConfig_DomainStrategy)(0), // 0: xray.proxy.wireguard.DeviceConfig.DomainStrategy - (*PeerConfig)(nil), // 1: xray.proxy.wireguard.PeerConfig - (*DeviceConfig)(nil), // 2: xray.proxy.wireguard.DeviceConfig +var file_proxy_wireguard_config_proto_goTypes = []interface{}{ + (*PeerConfig)(nil), // 0: xray.proxy.wireguard.PeerConfig + (*DeviceConfig)(nil), // 1: xray.proxy.wireguard.DeviceConfig } var file_proxy_wireguard_config_proto_depIdxs = []int32{ - 1, // 0: xray.proxy.wireguard.DeviceConfig.peers:type_name -> xray.proxy.wireguard.PeerConfig - 0, // 1: xray.proxy.wireguard.DeviceConfig.domain_strategy:type_name -> xray.proxy.wireguard.DeviceConfig.DomainStrategy - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 0, // 0: xray.proxy.wireguard.DeviceConfig.peers:type_name -> xray.proxy.wireguard.PeerConfig + 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_proxy_wireguard_config_proto_init() } @@ -350,19 +256,44 @@ func file_proxy_wireguard_config_proto_init() { if File_proxy_wireguard_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_proxy_wireguard_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_wireguard_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeviceConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proxy_wireguard_config_proto_rawDesc, - NumEnums: 1, + NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_wireguard_config_proto_goTypes, DependencyIndexes: file_proxy_wireguard_config_proto_depIdxs, - EnumInfos: file_proxy_wireguard_config_proto_enumTypes, MessageInfos: file_proxy_wireguard_config_proto_msgTypes, }.Build() File_proxy_wireguard_config_proto = out.File diff --git a/proxy/wireguard/config.proto b/proxy/wireguard/config.proto index aa05b822..810a1126 100644 --- a/proxy/wireguard/config.proto +++ b/proxy/wireguard/config.proto @@ -7,28 +7,18 @@ option java_package = "com.xray.proxy.wireguard"; option java_multiple_files = true; message PeerConfig { - string public_key = 1; - string pre_shared_key = 2; - string endpoint = 3; - uint32 keep_alive = 4; - repeated string allowed_ips = 5; + string public_key = 1; + string pre_shared_key = 2; + string endpoint = 3; + int32 keep_alive = 4; + repeated string allowed_ips = 5; } message DeviceConfig { - enum DomainStrategy { - FORCE_IP = 0; - FORCE_IP4 = 1; - FORCE_IP6 = 2; - FORCE_IP46 = 3; - FORCE_IP64 = 4; - } - string secret_key = 1; - repeated string endpoint = 2; - repeated PeerConfig peers = 3; - int32 mtu = 4; - int32 num_workers = 5; - bytes reserved = 6; - DomainStrategy domain_strategy = 7; - bool is_client = 8; - bool no_kernel_tun = 9; + string secret_key = 1; + repeated string endpoint = 2; + repeated PeerConfig peers = 3; + int32 mtu = 4; + int32 num_workers = 5; + bytes reserved = 6; } \ No newline at end of file diff --git a/proxy/wireguard/errors.generated.go b/proxy/wireguard/errors.generated.go new file mode 100644 index 00000000..8319e07d --- /dev/null +++ b/proxy/wireguard/errors.generated.go @@ -0,0 +1,9 @@ +package wireguard + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/wireguard/gvisortun/tun.go b/proxy/wireguard/gvisortun/tun.go deleted file mode 100644 index 2f9aa33c..00000000 --- a/proxy/wireguard/gvisortun/tun.go +++ /dev/null @@ -1,229 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. - */ - -package gvisortun - -import ( - "context" - "fmt" - "net/netip" - "os" - "sync" - "syscall" - - "golang.zx2c4.com/wireguard/tun" - "gvisor.dev/gvisor/pkg/buffer" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/link/channel" - "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" - "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" - "gvisor.dev/gvisor/pkg/tcpip/stack" - "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" - "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" - "gvisor.dev/gvisor/pkg/tcpip/transport/udp" -) - -type netTun struct { - ep *channel.Endpoint - stack *stack.Stack - events chan tun.Event - incomingPacket chan *buffer.View - mtu int - hasV4, hasV6 bool - closeOnce sync.Once -} - -type Net netTun - -func CreateNetTUN(localAddresses []netip.Addr, mtu int, promiscuousMode bool) (tun.Device, *Net, *stack.Stack, error) { - opts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol6, icmp.NewProtocol4}, - HandleLocal: !promiscuousMode, - } - dev := &netTun{ - ep: channel.New(1024, uint32(mtu), ""), - stack: stack.New(opts), - events: make(chan tun.Event, 1), - incomingPacket: make(chan *buffer.View), - mtu: mtu, - } - dev.ep.AddNotify(dev) - tcpipErr := dev.stack.CreateNIC(1, dev.ep) - if tcpipErr != nil { - return nil, nil, dev.stack, fmt.Errorf("CreateNIC: %v", tcpipErr) - } - for _, ip := range localAddresses { - var protoNumber tcpip.NetworkProtocolNumber - if ip.Is4() { - protoNumber = ipv4.ProtocolNumber - } else if ip.Is6() { - protoNumber = ipv6.ProtocolNumber - } - protoAddr := tcpip.ProtocolAddress{ - Protocol: protoNumber, - AddressWithPrefix: tcpip.AddrFromSlice(ip.AsSlice()).WithPrefix(), - } - tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{}) - if tcpipErr != nil { - return nil, nil, dev.stack, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr) - } - if ip.Is4() { - dev.hasV4 = true - } else if ip.Is6() { - dev.hasV6 = true - } - } - if dev.hasV4 { - dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1}) - } - if dev.hasV6 { - dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1}) - } - if promiscuousMode { - // enable promiscuous mode to handle all packets processed by netstack - dev.stack.SetPromiscuousMode(1, true) - dev.stack.SetSpoofing(1, true) - } - - opt := tcpip.CongestionControlOption("cubic") - if err := dev.stack.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { - return nil, nil, dev.stack, fmt.Errorf("SetTransportProtocolOption(%d, &%T(%s)): %s", tcp.ProtocolNumber, opt, opt, err) - } - - dev.events <- tun.EventUp - return dev, (*Net)(dev), dev.stack, nil -} - -// BatchSize implements tun.Device -func (tun *netTun) BatchSize() int { - return 1 -} - -// Name implements tun.Device -func (tun *netTun) Name() (string, error) { - return "go", nil -} - -// File implements tun.Device -func (tun *netTun) File() *os.File { - return nil -} - -// Events implements tun.Device -func (tun *netTun) Events() <-chan tun.Event { - return tun.events -} - -// Read implements tun.Device - -func (tun *netTun) Read(buf [][]byte, sizes []int, offset int) (int, error) { - view, ok := <-tun.incomingPacket - if !ok { - return 0, os.ErrClosed - } - - n, err := view.Read(buf[0][offset:]) - if err != nil { - return 0, err - } - sizes[0] = n - return 1, nil -} - -// Write implements tun.Device -func (tun *netTun) Write(buf [][]byte, offset int) (int, error) { - for _, buf := range buf { - packet := buf[offset:] - if len(packet) == 0 { - continue - } - - pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: buffer.MakeWithData(packet)}) - switch packet[0] >> 4 { - case 4: - tun.ep.InjectInbound(header.IPv4ProtocolNumber, pkb) - case 6: - tun.ep.InjectInbound(header.IPv6ProtocolNumber, pkb) - default: - return 0, syscall.EAFNOSUPPORT - } - } - return len(buf), nil -} - -// WriteNotify implements channel.Notification -func (tun *netTun) WriteNotify() { - pkt := tun.ep.Read() - if pkt == nil { - return - } - - view := pkt.ToView() - pkt.DecRef() - - tun.incomingPacket <- view -} - -// Flush implements tun.Device -func (tun *netTun) Flush() error { - return nil -} - -// Close implements tun.Device -func (tun *netTun) Close() error { - tun.closeOnce.Do(func() { - tun.stack.RemoveNIC(1) - - close(tun.events) - - tun.ep.Close() - - close(tun.incomingPacket) - }) - return nil -} - -// MTU implements tun.Device -func (tun *netTun) MTU() (int, error) { - return tun.mtu, nil -} - -func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) { - var protoNumber tcpip.NetworkProtocolNumber - if endpoint.Addr().Is4() { - protoNumber = ipv4.ProtocolNumber - } else { - protoNumber = ipv6.ProtocolNumber - } - return tcpip.FullAddress{ - NIC: 1, - Addr: tcpip.AddrFromSlice(endpoint.Addr().AsSlice()), - Port: endpoint.Port(), - }, protoNumber -} - -func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) { - fa, pn := convertToFullAddr(addr) - return gonet.DialContextTCP(ctx, net.stack, fa, pn) -} - -func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) { - var lfa, rfa *tcpip.FullAddress - var pn tcpip.NetworkProtocolNumber - if laddr.IsValid() || laddr.Port() > 0 { - var addr tcpip.FullAddress - addr, pn = convertToFullAddr(laddr) - lfa = &addr - } - if raddr.IsValid() || raddr.Port() > 0 { - var addr tcpip.FullAddress - addr, pn = convertToFullAddr(raddr) - rfa = &addr - } - return gonet.DialUDP(net.stack, lfa, rfa, pn) -} diff --git a/proxy/wireguard/server.go b/proxy/wireguard/server.go deleted file mode 100644 index bdf27568..00000000 --- a/proxy/wireguard/server.go +++ /dev/null @@ -1,194 +0,0 @@ -package wireguard - -import ( - "context" - goerrors "errors" - "io" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/log" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/signal" - "github.com/xtls/xray-core/common/task" - "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/features/dns" - "github.com/xtls/xray-core/features/policy" - "github.com/xtls/xray-core/features/routing" - "github.com/xtls/xray-core/transport/internet/stat" -) - -var nullDestination = net.TCPDestination(net.AnyIP, 0) - -type Server struct { - bindServer *netBindServer - - info routingInfo - policyManager policy.Manager -} - -type routingInfo struct { - ctx context.Context - dispatcher routing.Dispatcher - inboundTag *session.Inbound - outboundTag *session.Outbound - contentTag *session.Content -} - -func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) { - v := core.MustFromContext(ctx) - - endpoints, hasIPv4, hasIPv6, err := parseEndpoints(conf) - if err != nil { - return nil, err - } - - server := &Server{ - bindServer: &netBindServer{ - netBind: netBind{ - dns: v.GetFeature(dns.ClientType()).(dns.Client), - dnsOption: dns.IPOption{ - IPv4Enable: hasIPv4, - IPv6Enable: hasIPv6, - }, - }, - }, - policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), - } - - tun, err := conf.createTun()(endpoints, int(conf.Mtu), server.forwardConnection) - if err != nil { - return nil, err - } - - if err = tun.BuildDevice(createIPCRequest(conf), server.bindServer); err != nil { - _ = tun.Close() - return nil, err - } - - return server, nil -} - -// Network implements proxy.Inbound. -func (*Server) Network() []net.Network { - return []net.Network{net.Network_UDP} -} - -// Process implements proxy.Inbound. -func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { - inbound := session.InboundFromContext(ctx) - inbound.Name = "wireguard" - inbound.CanSpliceCopy = 3 - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - - s.info = routingInfo{ - ctx: core.ToBackgroundDetachedContext(ctx), - dispatcher: dispatcher, - inboundTag: session.InboundFromContext(ctx), - outboundTag: ob, - contentTag: session.ContentFromContext(ctx), - } - - ep, err := s.bindServer.ParseEndpoint(conn.RemoteAddr().String()) - if err != nil { - return err - } - - nep := ep.(*netEndpoint) - nep.conn = conn - - reader := buf.NewPacketReader(conn) - for { - mpayload, err := reader.ReadMultiBuffer() - if err != nil { - return err - } - - for _, payload := range mpayload { - v, ok := <-s.bindServer.readQueue - if !ok { - return nil - } - i, err := payload.Read(v.buff) - - v.bytes = i - v.endpoint = nep - v.err = err - v.waiter.Done() - if err != nil && goerrors.Is(err, io.EOF) { - nep.conn = nil - return nil - } - } - } -} - -func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) { - if s.info.dispatcher == nil { - errors.LogError(s.info.ctx, "unexpected: dispatcher == nil") - return - } - defer conn.Close() - - ctx, cancel := context.WithCancel(core.ToBackgroundDetachedContext(s.info.ctx)) - plcy := s.policyManager.ForLevel(0) - timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) - - ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ - From: nullDestination, - To: dest, - Status: log.AccessAccepted, - Reason: "", - }) - - if s.info.inboundTag != nil { - ctx = session.ContextWithInbound(ctx, s.info.inboundTag) - } - - // what's this? - // Session information should not be shared between different connections - // why reuse them in server level? This will cause incorrect destoverride and unexpected routing behavior. - // Disable it temporarily. Maybe s.info should be removed. - - // if s.info.outboundTag != nil { - // ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{s.info.outboundTag}) - // } - // if s.info.contentTag != nil { - // ctx = session.ContextWithContent(ctx, s.info.contentTag) - // } - - link, err := s.info.dispatcher.Dispatch(ctx, dest) - if err != nil { - errors.LogErrorInner(s.info.ctx, err, "dispatch connection") - } - defer cancel() - - requestDone := func() error { - defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) - if err := buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all TCP request").Base(err) - } - - return nil - } - - responseDone := func() error { - defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) - if err := buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)); err != nil { - return errors.New("failed to transport all TCP response").Base(err) - } - - return nil - } - - requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer)) - if err := task.Run(ctx, requestDonePost, responseDone); err != nil { - common.Interrupt(link.Reader) - common.Interrupt(link.Writer) - errors.LogDebugInner(s.info.ctx, err, "connection ends") - return - } -} diff --git a/proxy/wireguard/server_test.go b/proxy/wireguard/server_test.go deleted file mode 100644 index 057b508e..00000000 --- a/proxy/wireguard/server_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package wireguard_test - -import ( - "context" - "github.com/stretchr/testify/assert" - "runtime/debug" - "testing" - - "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/proxy/wireguard" -) - -// TestWireGuardServerInitializationError verifies that an error during TUN initialization -// (triggered by an empty SecretKey) in the WireGuard server does not cause a panic and returns an error instead. -func TestWireGuardServerInitializationError(t *testing.T) { - // Create a minimal core instance with default features - config := &core.Config{} - instance, err := core.New(config) - if err != nil { - t.Fatalf("Failed to create core instance: %v", err) - } - // Set the Xray instance in the context - ctx := context.WithValue(context.Background(), core.XrayKey(1), instance) - - // Define the server configuration with an empty SecretKey to trigger error - conf := &wireguard.DeviceConfig{ - IsClient: false, - Endpoint: []string{"10.0.0.1/32"}, - Mtu: 1420, - SecretKey: "", // Empty SecretKey to trigger error - Peers: []*wireguard.PeerConfig{ - { - PublicKey: "some_public_key", - AllowedIps: []string{"10.0.0.2/32"}, - }, - }, - } - - // Use defer to catch any panic and fail the test explicitly - defer func() { - if r := recover(); r != nil { - t.Errorf("TUN initialization panicked: %v", r) - debug.PrintStack() - } - }() - - // Attempt to initialize the WireGuard server - _, err = wireguard.NewServer(ctx, conf) - - // Check that an error is returned - assert.ErrorContains(t, err, "failed to set private_key: hex string does not fit the slice") -} diff --git a/proxy/wireguard/tun.go b/proxy/wireguard/tun.go index 74a3b71d..4d1cb7f6 100644 --- a/proxy/wireguard/tun.go +++ b/proxy/wireguard/tun.go @@ -1,3 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + package wireguard import ( @@ -5,201 +10,294 @@ import ( "fmt" "net" "net/netip" - "runtime" - "strconv" - "strings" - "sync" - "time" + "os" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/log" - xnet "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/proxy/wireguard/gvisortun" + "github.com/sagernet/wireguard-go/tun" + "github.com/xtls/xray-core/features/dns" + "gvisor.dev/gvisor/pkg/bufferv2" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/link/channel" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" + "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp" - "gvisor.dev/gvisor/pkg/waiter" - - "golang.zx2c4.com/wireguard/conn" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun" ) -type tunCreator func(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error) - -type promiscuousModeHandler func(dest xnet.Destination, conn net.Conn) - -type Tunnel interface { - BuildDevice(ipc string, bind conn.Bind) error - DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (net.Conn, error) - DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) - Close() error +type netTun struct { + ep *channel.Endpoint + stack *stack.Stack + events chan tun.Event + incomingPacket chan *bufferv2.View + mtu int + dnsClient dns.Client + hasV4, hasV6 bool } -type tunnel struct { - tun tun.Device - device *device.Device - rw sync.Mutex -} +type Net netTun -func (t *tunnel) BuildDevice(ipc string, bind conn.Bind) (err error) { - t.rw.Lock() - defer t.rw.Unlock() - - if t.device != nil { - return errors.New("device is already initialized") +func CreateNetTUN(localAddresses []netip.Addr, dnsClient dns.Client, mtu int) (tun.Device, *Net, error) { + opts := stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, + TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, + HandleLocal: true, } - - logger := &device.Logger{ - Verbosef: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Debug, - Content: fmt.Sprintf(format, args...), - }) - }, - Errorf: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Error, - Content: fmt.Sprintf(format, args...), - }) - }, + dev := &netTun{ + ep: channel.New(1024, uint32(mtu), ""), + stack: stack.New(opts), + events: make(chan tun.Event, 10), + incomingPacket: make(chan *bufferv2.View), + dnsClient: dnsClient, + mtu: mtu, } - - t.device = device.NewDevice(t.tun, bind, logger) - if err = t.device.IpcSet(ipc); err != nil { - return err + dev.ep.AddNotify(dev) + tcpipErr := dev.stack.CreateNIC(1, dev.ep) + if tcpipErr != nil { + return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr) } - if err = t.device.Up(); err != nil { - return err - } - return nil -} - -func (t *tunnel) Close() (err error) { - t.rw.Lock() - defer t.rw.Unlock() - - if t.device == nil { - return nil - } - - t.device.Close() - t.device = nil - err = t.tun.Close() - t.tun = nil - return nil -} - -func CalculateInterfaceName(name string) (tunName string) { - if runtime.GOOS == "darwin" { - tunName = "utun" - } else if name != "" { - tunName = name - } else { - tunName = "tun" - } - interfaces, err := net.Interfaces() - if err != nil { - return - } - var tunIndex int - for _, netInterface := range interfaces { - if strings.HasPrefix(netInterface.Name, tunName) { - index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) - if parseErr == nil { - tunIndex = int(index) + 1 - } + for _, ip := range localAddresses { + var protoNumber tcpip.NetworkProtocolNumber + if ip.Is4() { + protoNumber = ipv4.ProtocolNumber + } else if ip.Is6() { + protoNumber = ipv6.ProtocolNumber + } + protoAddr := tcpip.ProtocolAddress{ + Protocol: protoNumber, + AddressWithPrefix: tcpip.Address(ip.AsSlice()).WithPrefix(), + } + tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{}) + if tcpipErr != nil { + return nil, nil, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr) + } + if ip.Is4() { + dev.hasV4 = true + } else if ip.Is6() { + dev.hasV6 = true } } - tunName = fmt.Sprintf("%s%d", tunName, tunIndex) - return -} - -var _ Tunnel = (*gvisorNet)(nil) - -type gvisorNet struct { - tunnel - net *gvisortun.Net -} - -func (g *gvisorNet) Close() error { - return g.tunnel.Close() -} - -func (g *gvisorNet) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) ( - net.Conn, error, -) { - return g.net.DialContextTCPAddrPort(ctx, addr) -} - -func (g *gvisorNet) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) { - return g.net.DialUDPAddrPort(laddr, raddr) -} - -func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error) { - out := &gvisorNet{} - tun, n, stack, err := gvisortun.CreateNetTUN(localAddresses, mtu, handler != nil) - if err != nil { - return nil, err + if dev.hasV4 { + dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1}) + } + if dev.hasV6 { + dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1}) } - if handler != nil { - // handler is only used for promiscuous mode - // capture all packets and send to handler + dev.events <- tun.EventUp + return dev, (*Net)(dev), nil +} - tcpForwarder := tcp.NewForwarder(stack, 0, 65535, func(r *tcp.ForwarderRequest) { - go func(r *tcp.ForwarderRequest) { - var ( - wq waiter.Queue - id = r.ID() - ) +func (tun *netTun) Name() (string, error) { + return "go", nil +} - // Perform a TCP three-way handshake. - ep, err := r.CreateEndpoint(&wq) - if err != nil { - errors.LogError(context.Background(), err.String()) - r.Complete(true) - return - } - r.Complete(false) - defer ep.Close() +func (tun *netTun) File() *os.File { + return nil +} - // enable tcp keep-alive to prevent hanging connections - ep.SocketOptions().SetKeepAlive(true) +func (tun *netTun) Events() chan tun.Event { + return tun.events +} - // local address is actually destination - handler(xnet.TCPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewTCPConn(&wq, ep)) - }(r) - }) - stack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) - - udpForwarder := udp.NewForwarder(stack, func(r *udp.ForwarderRequest) { - go func(r *udp.ForwarderRequest) { - var ( - wq waiter.Queue - id = r.ID() - ) - - ep, err := r.CreateEndpoint(&wq) - if err != nil { - errors.LogError(context.Background(), err.String()) - return - } - defer ep.Close() - - // prevents hanging connections and ensure timely release - ep.SocketOptions().SetLinger(tcpip.LingerOption{ - Enabled: true, - Timeout: 15 * time.Second, - }) - - handler(xnet.UDPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewUDPConn(&wq, ep)) - }(r) - }) - stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket) +func (tun *netTun) Read(buf []byte, offset int) (int, error) { + view, ok := <-tun.incomingPacket + if !ok { + return 0, os.ErrClosed } - out.tun, out.net = tun, n - return out, nil + return view.Read(buf[offset:]) +} + +func (tun *netTun) Write(buf []byte, offset int) (int, error) { + packet := buf[offset:] + if len(packet) == 0 { + return 0, nil + } + + pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: bufferv2.MakeWithData(packet)}) + switch packet[0] >> 4 { + case 4: + tun.ep.InjectInbound(header.IPv4ProtocolNumber, pkb) + case 6: + tun.ep.InjectInbound(header.IPv6ProtocolNumber, pkb) + } + + return len(buf), nil +} + +func (tun *netTun) WriteNotify() { + pkt := tun.ep.Read() + if pkt == nil { + return + } + + view := pkt.ToView() + pkt.DecRef() + + tun.incomingPacket <- view +} + +func (tun *netTun) Flush() error { + return nil +} + +func (tun *netTun) Close() error { + tun.stack.RemoveNIC(1) + + if tun.events != nil { + close(tun.events) + } + + tun.ep.Close() + + if tun.incomingPacket != nil { + close(tun.incomingPacket) + } + + return nil +} + +func (tun *netTun) MTU() (int, error) { + return tun.mtu, nil +} + +func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) { + var protoNumber tcpip.NetworkProtocolNumber + if endpoint.Addr().Is4() { + protoNumber = ipv4.ProtocolNumber + } else { + protoNumber = ipv6.ProtocolNumber + } + return tcpip.FullAddress{ + NIC: 1, + Addr: tcpip.Address(endpoint.Addr().AsSlice()), + Port: endpoint.Port(), + }, protoNumber +} + +func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) { + fa, pn := convertToFullAddr(addr) + return gonet.DialContextTCP(ctx, net.stack, fa, pn) +} + +func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) { + if addr == nil { + return net.DialContextTCPAddrPort(ctx, netip.AddrPort{}) + } + ip, _ := netip.AddrFromSlice(addr.IP) + return net.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(ip, uint16(addr.Port))) +} + +func (net *Net) DialTCPAddrPort(addr netip.AddrPort) (*gonet.TCPConn, error) { + fa, pn := convertToFullAddr(addr) + return gonet.DialTCP(net.stack, fa, pn) +} + +func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) { + if addr == nil { + return net.DialTCPAddrPort(netip.AddrPort{}) + } + ip, _ := netip.AddrFromSlice(addr.IP) + return net.DialTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port))) +} + +func (net *Net) ListenTCPAddrPort(addr netip.AddrPort) (*gonet.TCPListener, error) { + fa, pn := convertToFullAddr(addr) + return gonet.ListenTCP(net.stack, fa, pn) +} + +func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) { + if addr == nil { + return net.ListenTCPAddrPort(netip.AddrPort{}) + } + ip, _ := netip.AddrFromSlice(addr.IP) + return net.ListenTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port))) +} + +func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) { + var lfa, rfa *tcpip.FullAddress + var pn tcpip.NetworkProtocolNumber + if laddr.IsValid() || laddr.Port() > 0 { + var addr tcpip.FullAddress + addr, pn = convertToFullAddr(laddr) + lfa = &addr + } + if raddr.IsValid() || raddr.Port() > 0 { + var addr tcpip.FullAddress + addr, pn = convertToFullAddr(raddr) + rfa = &addr + } + return gonet.DialUDP(net.stack, lfa, rfa, pn) +} + +func (net *Net) ListenUDPAddrPort(laddr netip.AddrPort) (*gonet.UDPConn, error) { + return net.DialUDPAddrPort(laddr, netip.AddrPort{}) +} + +func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) { + var la, ra netip.AddrPort + if laddr != nil { + ip, _ := netip.AddrFromSlice(laddr.IP) + la = netip.AddrPortFrom(ip, uint16(laddr.Port)) + } + if raddr != nil { + ip, _ := netip.AddrFromSlice(raddr.IP) + ra = netip.AddrPortFrom(ip, uint16(raddr.Port)) + } + return net.DialUDPAddrPort(la, ra) +} + +func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) { + return net.DialUDP(laddr, nil) +} + +func (n *Net) HasV4() bool { + return n.hasV4 +} + +func (n *Net) HasV6() bool { + return n.hasV6 +} + +func IsDomainName(s string) bool { + l := len(s) + if l == 0 || l > 254 || l == 254 && s[l-1] != '.' { + return false + } + last := byte('.') + nonNumeric := false + partlen := 0 + for i := 0; i < len(s); i++ { + c := s[i] + switch { + default: + return false + case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_': + nonNumeric = true + partlen++ + case '0' <= c && c <= '9': + partlen++ + case c == '-': + if last == '.' { + return false + } + partlen++ + nonNumeric = true + case c == '.': + if last == '.' || last == '-' { + return false + } + if partlen > 63 || partlen == 0 { + return false + } + partlen = 0 + } + last = c + } + if last == '-' || partlen > 63 { + return false + } + return nonNumeric } diff --git a/proxy/wireguard/tun_default.go b/proxy/wireguard/tun_default.go deleted file mode 100644 index 50a50944..00000000 --- a/proxy/wireguard/tun_default.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !linux || android - -package wireguard - -import ( - "errors" - "net/netip" -) - -func createKernelTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (t Tunnel, err error) { - return nil, errors.New("not implemented") -} - -func KernelTunSupported() (bool, error) { - return false, nil -} diff --git a/proxy/wireguard/tun_linux.go b/proxy/wireguard/tun_linux.go deleted file mode 100644 index 7a46138a..00000000 --- a/proxy/wireguard/tun_linux.go +++ /dev/null @@ -1,264 +0,0 @@ -//go:build linux && !android - -package wireguard - -import ( - "context" - goerrors "errors" - "fmt" - "net" - "net/netip" - "os" - "sync" - - "golang.org/x/sys/unix" - - "github.com/sagernet/sing/common/control" - "github.com/vishvananda/netlink" - "github.com/xtls/xray-core/common/errors" - wgtun "golang.zx2c4.com/wireguard/tun" -) - -type deviceNet struct { - tunnel - dialer net.Dialer - - handle *netlink.Handle - linkAddrs []netlink.Addr - routes []*netlink.Route - rules []*netlink.Rule -} - -var ( - tableIndex int = 10230 - mu sync.Mutex -) - -func allocateIPv6TableIndex() int { - mu.Lock() - defer mu.Unlock() - - if tableIndex > 10230 { - errors.LogInfo(context.Background(), "allocate new ipv6 table index: ", tableIndex) - } - currentIndex := tableIndex - tableIndex++ - return currentIndex -} - -func newDeviceNet(interfaceName string) *deviceNet { - var dialer net.Dialer - bindControl := control.BindToInterface(control.NewDefaultInterfaceFinder(), interfaceName, -1) - dialer.Control = control.Append(dialer.Control, bindControl) - return &deviceNet{dialer: dialer} -} - -func (d *deviceNet) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) ( - net.Conn, error, -) { - return d.dialer.DialContext(ctx, "tcp", addr.String()) -} - -func (d *deviceNet) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) { - dialer := d.dialer - dialer.LocalAddr = &net.UDPAddr{IP: laddr.Addr().AsSlice(), Port: int(laddr.Port())} - return dialer.DialContext(context.Background(), "udp", raddr.String()) -} - -func (d *deviceNet) Close() (err error) { - var errs []error - for _, rule := range d.rules { - if err = d.handle.RuleDel(rule); err != nil { - errs = append(errs, fmt.Errorf("failed to delete rule: %w", err)) - } - } - for _, route := range d.routes { - if err = d.handle.RouteDel(route); err != nil { - errs = append(errs, fmt.Errorf("failed to delete route: %w", err)) - } - } - if err = d.tunnel.Close(); err != nil { - errs = append(errs, fmt.Errorf("failed to close tunnel: %w", err)) - } - if d.handle != nil { - d.handle.Close() - d.handle = nil - } - if len(errs) == 0 { - return nil - } - return goerrors.Join(errs...) -} - -func createKernelTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (t Tunnel, err error) { - if handler != nil { - return nil, errors.New("TODO: support promiscuous mode") - } - - var v4, v6 *netip.Addr - for _, prefixes := range localAddresses { - if v4 == nil && prefixes.Is4() { - x := prefixes - v4 = &x - } - if v6 == nil && prefixes.Is6() { - x := prefixes - v6 = &x - } - } - - writeSysctlZero := func(path string) error { - _, err := os.Stat(path) - if os.IsNotExist(err) { - return nil - } - if err != nil { - return err - } - return os.WriteFile(path, []byte("0"), 0o644) - } - - // system configs. - if v4 != nil { - if err = writeSysctlZero("/proc/sys/net/ipv4/conf/all/rp_filter"); err != nil { - return nil, fmt.Errorf("failed to disable ipv4 rp_filter for all: %w", err) - } - } - if v6 != nil { - if err = writeSysctlZero("/proc/sys/net/ipv6/conf/all/disable_ipv6"); err != nil { - return nil, fmt.Errorf("failed to enable ipv6: %w", err) - } - if err = writeSysctlZero("/proc/sys/net/ipv6/conf/all/rp_filter"); err != nil { - return nil, fmt.Errorf("failed to disable ipv6 rp_filter for all: %w", err) - } - } - - n := CalculateInterfaceName("wg") - wgt, err := wgtun.CreateTUN(n, mtu) - if err != nil { - return nil, err - } - defer func() { - if err != nil { - _ = wgt.Close() - } - }() - - // disable linux rp_filter for tunnel device to avoid packet drop. - // the operation require root privilege on container require '--privileged' flag. - if v4 != nil { - if err = writeSysctlZero("/proc/sys/net/ipv4/conf/" + n + "/rp_filter"); err != nil { - return nil, fmt.Errorf("failed to disable ipv4 rp_filter for tunnel: %w", err) - } - } - if v6 != nil { - if err = writeSysctlZero("/proc/sys/net/ipv6/conf/" + n + "/rp_filter"); err != nil { - return nil, fmt.Errorf("failed to disable ipv6 rp_filter for tunnel: %w", err) - } - } - - ipv6TableIndex := allocateIPv6TableIndex() - if v6 != nil { - r := &netlink.Route{Table: ipv6TableIndex} - for { - routeList, fErr := netlink.RouteListFiltered(netlink.FAMILY_V6, r, netlink.RT_FILTER_TABLE) - if len(routeList) == 0 || fErr != nil { - break - } - ipv6TableIndex-- - if ipv6TableIndex < 0 { - return nil, fmt.Errorf("failed to find available ipv6 table index") - } - } - } - - out := newDeviceNet(n) - out.handle, err = netlink.NewHandle() - if err != nil { - return nil, err - } - defer func() { - if err != nil { - _ = out.Close() - } - }() - - l, err := netlink.LinkByName(n) - if err != nil { - return nil, err - } - - if v4 != nil { - addr := netlink.Addr{ - IPNet: &net.IPNet{ - IP: v4.AsSlice(), - Mask: net.CIDRMask(v4.BitLen(), v4.BitLen()), - }, - } - out.linkAddrs = append(out.linkAddrs, addr) - } - if v6 != nil { - addr := netlink.Addr{ - IPNet: &net.IPNet{ - IP: v6.AsSlice(), - Mask: net.CIDRMask(v6.BitLen(), v6.BitLen()), - }, - } - out.linkAddrs = append(out.linkAddrs, addr) - - rt := &netlink.Route{ - LinkIndex: l.Attrs().Index, - Dst: &net.IPNet{ - IP: net.IPv6zero, - Mask: net.CIDRMask(0, 128), - }, - Table: ipv6TableIndex, - } - out.routes = append(out.routes, rt) - - r := netlink.NewRule() - r.Table, r.Family, r.Src = ipv6TableIndex, unix.AF_INET6, addr.IPNet - out.rules = append(out.rules, r) - r = netlink.NewRule() - r.Table, r.Family, r.OifName = ipv6TableIndex, unix.AF_INET6, n - out.rules = append(out.rules, r) - } - - for _, addr := range out.linkAddrs { - if err = out.handle.AddrAdd(l, &addr); err != nil { - return nil, fmt.Errorf("failed to add address %s to %s: %w", addr, n, err) - } - } - if err = out.handle.LinkSetMTU(l, mtu); err != nil { - return nil, err - } - if err = out.handle.LinkSetUp(l); err != nil { - return nil, err - } - - for _, route := range out.routes { - if err = out.handle.RouteAdd(route); err != nil { - return nil, fmt.Errorf("failed to add route %s: %w", route, err) - } - } - for _, rule := range out.rules { - if err = out.handle.RuleAdd(rule); err != nil { - return nil, fmt.Errorf("failed to add rule %s: %w", rule, err) - } - } - out.tun = wgt - return out, nil -} - -func KernelTunSupported() (bool, error) { - var hdr unix.CapUserHeader - hdr.Version = unix.LINUX_CAPABILITY_VERSION_3 - hdr.Pid = 0 // 0 means current process - - var data unix.CapUserData - if err := unix.Capget(&hdr, &data); err != nil { - return false, fmt.Errorf("failed to get capabilities: %v", err) - } - - return (data.Effective & (1 << unix.CAP_NET_ADMIN)) != 0, nil -} diff --git a/proxy/wireguard/wireguard.go b/proxy/wireguard/wireguard.go index 0d75ee00..2b7e1c87 100644 --- a/proxy/wireguard/wireguard.go +++ b/proxy/wireguard/wireguard.go @@ -1,110 +1,264 @@ +/* + +Some of codes are copied from https://github.com/octeep/wireproxy, license below. + +Copyright (c) 2022 Wind T.F. Wong + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + package wireguard import ( + "bytes" "context" - "errors" "fmt" "net/netip" "strings" + "github.com/sagernet/wireguard-go/device" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/log" - "golang.zx2c4.com/wireguard/device" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/protocol" + "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/core" + "github.com/xtls/xray-core/features/dns" + "github.com/xtls/xray-core/features/policy" + "github.com/xtls/xray-core/transport" + "github.com/xtls/xray-core/transport/internet" ) -var wgLogger = &device.Logger{ - Verbosef: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Debug, - Content: fmt.Sprintf(format, args...), - }) - }, - Errorf: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Error, - Content: fmt.Sprintf(format, args...), - }) - }, +// Handler is an outbound connection that silently swallow the entire payload. +type Handler struct { + conf *DeviceConfig + net *Net + bind *netBindClient + policyManager policy.Manager + dns dns.Client + // cached configuration + ipc string + endpoints []netip.Addr } -func init() { - common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - deviceConfig := config.(*DeviceConfig) - if deviceConfig.IsClient { - return New(ctx, deviceConfig) - } else { - return NewServer(ctx, deviceConfig) +// New creates a new wireguard handler. +func New(ctx context.Context, conf *DeviceConfig) (*Handler, error) { + v := core.MustFromContext(ctx) + + endpoints, err := parseEndpoints(conf) + if err != nil { + return nil, err + } + + return &Handler{ + conf: conf, + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + dns: v.GetFeature(dns.ClientType()).(dns.Client), + ipc: createIPCRequest(conf), + endpoints: endpoints, + }, nil +} + +// Process implements OutboundHandler.Dispatch(). +func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { + if h.bind == nil || h.bind.dialer != dialer || h.net == nil { + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Info, + Content: "switching dialer", + }) + // bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer + bind := &netBindClient{ + dialer: dialer, + workers: int(h.conf.NumWorkers), + dns: h.dns, + reserved: h.conf.Reserved, } - })) + + net, err := h.makeVirtualTun(bind) + if err != nil { + bind.Close() + return newError("failed to create virtual tun interface").Base(err) + } + + h.net = net + if h.bind != nil { + h.bind.Close() + } + h.bind = bind + } + + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified") + } + // Destination of the inner request. + destination := outbound.Target + command := protocol.RequestCommandTCP + if destination.Network == net.Network_UDP { + command = protocol.RequestCommandUDP + } + + // resolve dns + addr := destination.Address + if addr.Family().IsDomain() { + ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ + IPv4Enable: h.net.HasV4(), + IPv6Enable: h.net.HasV6(), + }) + if err != nil { + return newError("failed to lookup DNS").Base(err) + } else if len(ips) == 0 { + return dns.ErrEmptyResponse + } + addr = net.IPAddress(ips[0]) + } + + p := h.policyManager.ForLevel(0) + + ctx, cancel := context.WithCancel(ctx) + timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle) + addrPort := netip.AddrPortFrom(toNetIpAddr(addr), destination.Port.Value()) + + var requestFunc func() error + var responseFunc func() error + + if command == protocol.RequestCommandTCP { + conn, err := h.net.DialContextTCPAddrPort(ctx, addrPort) + if err != nil { + return newError("failed to create TCP connection").Base(err) + } + + requestFunc = func() error { + defer timer.SetTimeout(p.Timeouts.DownlinkOnly) + return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) + } + responseFunc = func() error { + defer timer.SetTimeout(p.Timeouts.UplinkOnly) + return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) + } + } else if command == protocol.RequestCommandUDP { + conn, err := h.net.DialUDPAddrPort(netip.AddrPort{}, addrPort) + if err != nil { + return newError("failed to create UDP connection").Base(err) + } + + requestFunc = func() error { + defer timer.SetTimeout(p.Timeouts.DownlinkOnly) + return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) + } + responseFunc = func() error { + defer timer.SetTimeout(p.Timeouts.UplinkOnly) + return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) + } + } + + responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) + if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { + return newError("connection ends").Base(err) + } + + return nil +} + +// serialize the config into an IPC request +func createIPCRequest(conf *DeviceConfig) string { + var request bytes.Buffer + + request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey)) + + for _, peer := range conf.Peers { + request.WriteString(fmt.Sprintf("public_key=%s\nendpoint=%s\npersistent_keepalive_interval=%d\npreshared_key=%s\n", + peer.PublicKey, peer.Endpoint, peer.KeepAlive, peer.PreSharedKey)) + + for _, ip := range peer.AllowedIps { + request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip)) + } + } + + return request.String()[:request.Len()] } // convert endpoint string to netip.Addr -func parseEndpoints(conf *DeviceConfig) ([]netip.Addr, bool, bool, error) { - var hasIPv4, hasIPv6 bool - +func parseEndpoints(conf *DeviceConfig) ([]netip.Addr, error) { endpoints := make([]netip.Addr, len(conf.Endpoint)) for i, str := range conf.Endpoint { var addr netip.Addr if strings.Contains(str, "/") { prefix, err := netip.ParsePrefix(str) if err != nil { - return nil, false, false, err + return nil, err } addr = prefix.Addr() if prefix.Bits() != addr.BitLen() { - return nil, false, false, errors.New("interface address subnet should be /32 for IPv4 and /128 for IPv6") + return nil, newError("interface address subnet should be /32 for IPv4 and /128 for IPv6") } } else { var err error addr, err = netip.ParseAddr(str) if err != nil { - return nil, false, false, err + return nil, err } } endpoints[i] = addr - - if addr.Is4() { - hasIPv4 = true - } else if addr.Is6() { - hasIPv6 = true - } } - return endpoints, hasIPv4, hasIPv6, nil + return endpoints, nil } -// serialize the config into an IPC request -func createIPCRequest(conf *DeviceConfig) string { - var request strings.Builder - - request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey)) - - if !conf.IsClient { - // placeholder, we'll handle actual port listening on Xray - request.WriteString("listen_port=1337\n") +// creates a tun interface on netstack given a configuration +func (h *Handler) makeVirtualTun(bind *netBindClient) (*Net, error) { + tun, tnet, err := CreateNetTUN(h.endpoints, h.dns, int(h.conf.Mtu)) + if err != nil { + return nil, err } - for _, peer := range conf.Peers { - if peer.PublicKey != "" { - request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey)) - } + bind.dnsOption.IPv4Enable = tnet.HasV4() + bind.dnsOption.IPv6Enable = tnet.HasV6() - if peer.PreSharedKey != "" { - request.WriteString(fmt.Sprintf("preshared_key=%s\n", peer.PreSharedKey)) - } - - if peer.Endpoint != "" { - request.WriteString(fmt.Sprintf("endpoint=%s\n", peer.Endpoint)) - } - - for _, ip := range peer.AllowedIps { - request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip)) - } - - if peer.KeepAlive != 0 { - request.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", peer.KeepAlive)) - } + // dev := device.NewDevice(tun, conn.NewDefaultBind(), nil /* device.NewLogger(device.LogLevelVerbose, "") */) + dev := device.NewDevice(tun, bind, &device.Logger{ + Verbosef: func(format string, args ...any) { + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Debug, + Content: fmt.Sprintf(format, args...), + }) + }, + Errorf: func(format string, args ...any) { + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Error, + Content: fmt.Sprintf(format, args...), + }) + }, + }, int(h.conf.NumWorkers)) + err = dev.IpcSet(h.ipc) + if err != nil { + return nil, err } - return request.String()[:request.Len()] + err = dev.Up() + if err != nil { + return nil, err + } + + return tnet, nil +} + +func init() { + common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return New(ctx, config.(*DeviceConfig)) + })) } diff --git a/testing/mocks/dns.go b/testing/mocks/dns.go index fb398366..15ddfbf4 100644 --- a/testing/mocks/dns.go +++ b/testing/mocks/dns.go @@ -50,13 +50,12 @@ func (mr *DNSClientMockRecorder) Close() *gomock.Call { } // LookupIP mocks base method -func (m *DNSClient) LookupIP(arg0 string, arg1 dns.IPOption) ([]net.IP, uint32, error) { +func (m *DNSClient) LookupIP(arg0 string, arg1 dns.IPOption) ([]net.IP, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LookupIP", arg0, arg1) ret0, _ := ret[0].([]net.IP) - ret1, _ := ret[1].(uint32) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret1, _ := ret[1].(error) + return ret0, ret1 } // LookupIP indicates an expected call of LookupIP diff --git a/testing/mocks/outbound.go b/testing/mocks/outbound.go index f6352bff..4f1083cf 100644 --- a/testing/mocks/outbound.go +++ b/testing/mocks/outbound.go @@ -91,20 +91,6 @@ func (mr *OutboundManagerMockRecorder) GetHandler(arg0 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandler", reflect.TypeOf((*OutboundManager)(nil).GetHandler), arg0) } -// ListHandlers mocks base method -func (m *OutboundManager) ListHandlers(arg0 context.Context) []outbound.Handler { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListHandlers", arg0) - ret0, _ := ret[0].([]outbound.Handler) - return ret0 -} - -// ListHandlers indicates an expected call of ListHandlers -func (mr *OutboundManagerMockRecorder) ListHandlers(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListHandlers", reflect.TypeOf((*OutboundManager)(nil).ListHandlers), arg0) -} - // RemoveHandler mocks base method func (m *OutboundManager) RemoveHandler(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index 8ee9e4bf..d82e6a37 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -30,84 +30,8 @@ import ( "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/protobuf/testing/protocmp" ) -func TestCommanderListenConfigurationItem(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - clientPort := tcp.PickPort() - cmdPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&commander.Config{ - Tag: "api", - Listen: fmt.Sprintf("127.0.0.1:%d", cmdPort), - Service: []*serial.TypedMessage{ - serial.ToTypedMessage(&command.Config{}), - }, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - Tag: "d", - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - Tag: "default-outbound", - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - servers, err := InitializeServerConfigs(clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil { - t.Fatal(err) - } - - cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) - common.Must(err) - defer cmdConn.Close() - - hsClient := command.NewHandlerServiceClient(cmdConn) - resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{ - Tag: "d", - }) - common.Must(err) - if resp == nil { - t.Error("unexpected nil response") - } - - { - _, err := net.DialTCP("tcp", nil, &net.TCPAddr{ - IP: []byte{127, 0, 0, 1}, - Port: int(clientPort), - }) - if err == nil { - t.Error("unexpected nil error") - } - } -} - func TestCommanderRemoveHandler(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, @@ -179,7 +103,7 @@ func TestCommanderRemoveHandler(t *testing.T) { t.Fatal(err) } - cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) common.Must(err) defer cmdConn.Close() @@ -203,116 +127,6 @@ func TestCommanderRemoveHandler(t *testing.T) { } } -func TestCommanderListHandlers(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - clientPort := tcp.PickPort() - cmdPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&commander.Config{ - Tag: "api", - Service: []*serial.TypedMessage{ - serial.ToTypedMessage(&command.Config{}), - }, - }), - serial.ToTypedMessage(&router.Config{ - Rule: []*router.RoutingRule{ - { - InboundTag: []string{"api"}, - TargetTag: &router.RoutingRule_Tag{ - Tag: "api", - }, - }, - }, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - Tag: "d", - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - { - Tag: "api", - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(cmdPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - Tag: "default-outbound", - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{}), - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - servers, err := InitializeServerConfigs(clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil { - t.Fatal(err) - } - - cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) - common.Must(err) - defer cmdConn.Close() - - hsClient := command.NewHandlerServiceClient(cmdConn) - inboundResp, err := hsClient.ListInbounds(context.Background(), &command.ListInboundsRequest{}) - common.Must(err) - if inboundResp == nil { - t.Error("unexpected nil response") - } - - if diff := cmp.Diff( - inboundResp.Inbounds, - clientConfig.Inbound, - protocmp.Transform(), - cmpopts.SortSlices(func(a, b *core.InboundHandlerConfig) bool { - return a.Tag < b.Tag - })); diff != "" { - t.Fatalf("inbound response doesn't match config (-want +got):\n%s", diff) - } - - outboundResp, err := hsClient.ListOutbounds(context.Background(), &command.ListOutboundsRequest{}) - common.Must(err) - if outboundResp == nil { - t.Error("unexpected nil response") - } - - if diff := cmp.Diff( - outboundResp.Outbounds, - clientConfig.Outbound, - protocmp.Transform(), - cmpopts.SortSlices(func(a, b *core.InboundHandlerConfig) bool { - return a.Tag < b.Tag - })); diff != "" { - t.Fatalf("outbound response doesn't match config (-want +got):\n%s", diff) - } -} - func TestCommanderAddRemoveUser(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, @@ -414,9 +228,11 @@ func TestCommanderAddRemoveUser(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -454,7 +270,7 @@ func TestCommanderAddRemoveUser(t *testing.T) { t.Fatal("expected error: ", err) } - cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) common.Must(err) defer cmdConn.Close() @@ -569,9 +385,11 @@ func TestCommanderStats(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -591,9 +409,11 @@ func TestCommanderStats(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -631,7 +451,7 @@ func TestCommanderStats(t *testing.T) { t.Fatal(err) } - cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) common.Must(err) defer cmdConn.Close() diff --git a/testing/scenarios/common.go b/testing/scenarios/common.go index dc54105a..f011a64f 100644 --- a/testing/scenarios/common.go +++ b/testing/scenarios/common.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "github.com/golang/protobuf/proto" "github.com/xtls/xray-core/app/dispatcher" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/common" @@ -24,7 +25,6 @@ import ( "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/units" core "github.com/xtls/xray-core/core" - "google.golang.org/protobuf/proto" ) func xor(b []byte) []byte { @@ -96,7 +96,6 @@ func InitializeServerConfig(config *core.Config) (*exec.Cmd, error) { var ( testBinaryPath string - testBinaryCleanFn func() testBinaryPathGen sync.Once ) @@ -109,7 +108,6 @@ func genTestBinaryPath() { return err } tempDir = dir - testBinaryCleanFn = func() { os.RemoveAll(dir) } return nil })) file := filepath.Join(tempDir, "xray.test") diff --git a/testing/scenarios/dns_test.go b/testing/scenarios/dns_test.go index 1c6d8277..3280cbec 100644 --- a/testing/scenarios/dns_test.go +++ b/testing/scenarios/dns_test.go @@ -31,26 +31,18 @@ func TestResolveIP(t *testing.T) { serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&dns.Config{ - StaticHosts: []*dns.Config_HostMapping{ - { - Type: dns.DomainMatchingType_Full, - Domain: "google.com", - Ip: [][]byte{dest.Address.IP()}, - }, + Hosts: map[string]*net.IPOrDomain{ + "google.com": net.NewIPOrDomain(dest.Address), }, }), serial.ToTypedMessage(&router.Config{ DomainStrategy: router.Config_IpIfNonMatch, Rule: []*router.RoutingRule{ { - Geoip: []*router.GeoIP{ + Cidr: []*router.CIDR{ { - Cidr: []*router.CIDR{ - { - Ip: []byte{127, 0, 0, 0}, - Prefix: 8, - }, - }, + Ip: []byte{127, 0, 0, 0}, + Prefix: 8, }, }, TargetTag: &router.RoutingRule_Tag{ diff --git a/testing/scenarios/dokodemo_test.go b/testing/scenarios/dokodemo_test.go index f74fb4bd..69032b6e 100644 --- a/testing/scenarios/dokodemo_test.go +++ b/testing/scenarios/dokodemo_test.go @@ -85,9 +85,11 @@ func TestDokodemoTCP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -169,7 +171,7 @@ func TestDokodemoUDP(t *testing.T) { common.Must(err) defer CloseServer(server) - clientPortRange := uint32(3) + clientPortRange := uint32(5) retry := 1 clientPort := uint32(udp.PickPort()) for { @@ -181,9 +183,11 @@ func TestDokodemoUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, diff --git a/testing/scenarios/feature_test.go b/testing/scenarios/feature_test.go index 6fe0d850..bee4598f 100644 --- a/testing/scenarios/feature_test.go +++ b/testing/scenarios/feature_test.go @@ -53,9 +53,11 @@ func TestPassiveConnection(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -161,9 +163,11 @@ func TestProxy(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -237,7 +241,7 @@ func TestProxyOverKCP(t *testing.T) { PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ @@ -283,7 +287,7 @@ func TestProxyOverKCP(t *testing.T) { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, }, }), }, @@ -299,9 +303,11 @@ func TestProxyOverKCP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -327,7 +333,7 @@ func TestProxyOverKCP(t *testing.T) { Tag: "proxy", }, StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, }, }), }, @@ -386,9 +392,11 @@ func TestBlackhole(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, { @@ -397,9 +405,11 @@ func TestBlackhole(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest2.Address), - Port: uint32(dest2.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest2.Address), + Port: uint32(dest2.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -420,9 +430,7 @@ func TestBlackhole(t *testing.T) { TargetTag: &router.RoutingRule_Tag{ Tag: "blocked", }, - PortList: &net.PortList{ - Range: []*net.PortRange{net.SinglePortRange(dest2.Port)}, - }, + PortRange: net.SinglePortRange(dest2.Port), }, }, }), @@ -512,9 +520,11 @@ func TestUDPConnection(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, @@ -550,15 +560,16 @@ func TestDomainSniffing(t *testing.T) { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(sniffingPort)}}, Listen: net.NewIPOrDomain(net.LocalHostIP), - SniffingSettings: &proxyman.SniffingConfig{ - Enabled: true, - DestinationOverride: []string{"tls"}, + DomainOverride: []proxyman.KnownProtocols{ + proxyman.KnownProtocols_TLS, }, }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: 443, - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: 443, + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, { diff --git a/testing/scenarios/http_test.go b/testing/scenarios/http_test.go index b9b112ff..d6a765bb 100644 --- a/testing/scenarios/http_test.go +++ b/testing/scenarios/http_test.go @@ -7,7 +7,6 @@ import ( "io" "net/http" "net/url" - "strings" "testing" "time" @@ -129,8 +128,9 @@ func TestHttpError(t *testing.T) { } resp, err := client.Get("http://127.0.0.1:" + dest.Port.String()) - if resp != nil && resp.StatusCode != 503 || err != nil && !strings.Contains(err.Error(), "malformed HTTP status code") { - t.Error("should not receive http response", err) + common.Must(err) + if resp.StatusCode != 503 { + t.Error("status: ", resp.StatusCode) } } } diff --git a/testing/scenarios/main_test.go b/testing/scenarios/main_test.go deleted file mode 100644 index 269081c6..00000000 --- a/testing/scenarios/main_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package scenarios - -import ( - "testing" -) - -func TestMain(m *testing.M) { - genTestBinaryPath() - defer testBinaryCleanFn() - - m.Run() -} diff --git a/testing/scenarios/metrics_test.go b/testing/scenarios/metrics_test.go index eee2d942..914e46c4 100644 --- a/testing/scenarios/metrics_test.go +++ b/testing/scenarios/metrics_test.go @@ -3,7 +3,7 @@ package scenarios import ( "encoding/json" "fmt" - "io" + "io/ioutil" "net/http" "testing" @@ -80,7 +80,7 @@ func TestMetrics(t *testing.T) { if resp.StatusCode != http.StatusOK { t.Error("unexpected pprof status code") } - body, err := io.ReadAll(resp.Body) + body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } @@ -96,7 +96,7 @@ func TestMetrics(t *testing.T) { if resp2.StatusCode != http.StatusOK { t.Error("unexpected expvars status code") } - body2, err2 := io.ReadAll(resp2.Body) + body2, err2 := ioutil.ReadAll(resp2.Body) if err2 != nil { t.Fatal(err2) } diff --git a/testing/scenarios/policy_test.go b/testing/scenarios/policy_test.go index 1e8aafea..e45748d5 100644 --- a/testing/scenarios/policy_test.go +++ b/testing/scenarios/policy_test.go @@ -110,9 +110,11 @@ func TestVMessClosing(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -214,9 +216,11 @@ func TestZeroBuffer(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, diff --git a/testing/scenarios/reverse_test.go b/testing/scenarios/reverse_test.go index faaa402e..a87020b6 100644 --- a/testing/scenarios/reverse_test.go +++ b/testing/scenarios/reverse_test.go @@ -76,9 +76,11 @@ func TestReverseProxy(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, { @@ -141,9 +143,11 @@ func TestReverseProxy(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -255,9 +259,11 @@ func TestReverseProxyLongRunning(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, { @@ -334,9 +340,11 @@ func TestReverseProxyLongRunning(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, diff --git a/testing/scenarios/shadowsocks_2022_test.go b/testing/scenarios/shadowsocks_2022_test.go index f06c38dc..aa595844 100644 --- a/testing/scenarios/shadowsocks_2022_test.go +++ b/testing/scenarios/shadowsocks_2022_test.go @@ -32,22 +32,14 @@ func TestShadowsocks2022Tcp(t *testing.T) { } } -func TestShadowsocks2022UdpAES128(t *testing.T) { - password := make([]byte, 32) - rand.Read(password) - testShadowsocks2022Udp(t, shadowaead_2022.List[0], base64.StdEncoding.EncodeToString(password)) -} - -func TestShadowsocks2022UdpAES256(t *testing.T) { - password := make([]byte, 32) - rand.Read(password) - testShadowsocks2022Udp(t, shadowaead_2022.List[1], base64.StdEncoding.EncodeToString(password)) -} - -func TestShadowsocks2022UdpChacha(t *testing.T) { - password := make([]byte, 32) - rand.Read(password) - testShadowsocks2022Udp(t, shadowaead_2022.List[2], base64.StdEncoding.EncodeToString(password)) +func TestShadowsocks2022Udp(t *testing.T) { + for _, method := range shadowaead_2022.List { + password := make([]byte, 32) + rand.Read(password) + t.Run(method, func(t *testing.T) { + testShadowsocks2022Udp(t, method, base64.StdEncoding.EncodeToString(password)) + }) + } } func testShadowsocks2022Tcp(t *testing.T, method string, password string) { @@ -207,7 +199,7 @@ func testShadowsocks2022Udp(t *testing.T, method string, password string) { defer CloseAllServers(servers) var errGroup errgroup.Group - for i := 0; i < 2; i++ { + for i := 0; i < 10; i++ { errGroup.Go(testUDPConn(udpClientPort, 1024, time.Second*5)) } diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go index e7620429..d6b8ee82 100644 --- a/testing/scenarios/shadowsocks_test.go +++ b/testing/scenarios/shadowsocks_test.go @@ -289,7 +289,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { defer CloseAllServers(servers) var errGroup errgroup.Group - for i := 0; i < 2; i++ { + for i := 0; i < 10; i++ { errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errGroup.Wait(); err != nil { @@ -391,7 +391,7 @@ func TestShadowsocksAES128GCMUDPMux(t *testing.T) { defer CloseAllServers(servers) var errGroup errgroup.Group - for i := 0; i < 2; i++ { + for i := 0; i < 10; i++ { errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errGroup.Wait(); err != nil { diff --git a/testing/scenarios/socks_test.go b/testing/scenarios/socks_test.go index 83ebebc3..cb158c58 100644 --- a/testing/scenarios/socks_test.go +++ b/testing/scenarios/socks_test.go @@ -14,7 +14,6 @@ import ( "github.com/xtls/xray-core/proxy/blackhole" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/http" "github.com/xtls/xray-core/proxy/socks" "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/testing/servers/udp" @@ -64,9 +63,11 @@ func TestSocksBridgeTCP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -101,85 +102,6 @@ func TestSocksBridgeTCP(t *testing.T) { } } -func TestSocksWithHttpRequest(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ - AuthType: socks.AuthType_PASSWORD, - Accounts: map[string]string{ - "Test Account": "Test Password", - }, - Address: net.NewIPOrDomain(net.LocalHostIP), - UdpEnabled: false, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&http.ClientConfig{ - Server: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&http.Account{ - Username: "Test Account", - Password: "Test Password", - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != nil { - t.Error(err) - } -} - func TestSocksBridageUDP(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, @@ -213,9 +135,11 @@ func TestSocksBridageUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, @@ -247,9 +171,11 @@ func TestSocksBridageUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, @@ -328,9 +254,11 @@ func TestSocksBridageUDPWithRouting(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, @@ -366,9 +294,11 @@ func TestSocksBridageUDPWithRouting(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index a8abccd7..0b752ecf 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -23,6 +23,7 @@ import ( "github.com/xtls/xray-core/testing/servers/udp" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/grpc" + "github.com/xtls/xray-core/transport/internet/http" "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/websocket" "golang.org/x/sync/errgroup" @@ -80,9 +81,11 @@ func TestSimpleTLSConnection(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -195,9 +198,11 @@ func TestAutoIssuingCertificate(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -264,7 +269,7 @@ func TestTLSOverKCP(t *testing.T) { PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*serial.TypedMessage{ serial.ToTypedMessage(&tls.Config{ @@ -300,9 +305,11 @@ func TestTLSOverKCP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -325,7 +332,7 @@ func TestTLSOverKCP(t *testing.T) { }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*serial.TypedMessage{ serial.ToTypedMessage(&tls.Config{ @@ -364,7 +371,7 @@ func TestTLSOverWebSocket(t *testing.T) { PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ - ProtocolName: "websocket", + Protocol: internet.TransportProtocol_WebSocket, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*serial.TypedMessage{ serial.ToTypedMessage(&tls.Config{ @@ -400,9 +407,11 @@ func TestTLSOverWebSocket(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -425,11 +434,11 @@ func TestTLSOverWebSocket(t *testing.T) { }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ - ProtocolName: "websocket", + Protocol: internet.TransportProtocol_WebSocket, TransportSettings: []*internet.TransportConfig{ { - ProtocolName: "websocket", - Settings: serial.ToTypedMessage(&websocket.Config{}), + Protocol: internet.TransportProtocol_WebSocket, + Settings: serial.ToTypedMessage(&websocket.Config{}), }, }, SecurityType: serial.GetMessageType(&tls.Config{}), @@ -457,6 +466,130 @@ func TestTLSOverWebSocket(t *testing.T) { } } +func TestHTTP2(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := tcp.PickPort() + serverConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_HTTP, + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_HTTP, + Settings: serial.ToTypedMessage(&http.Config{ + Host: []string{"example.com"}, + Path: "/testpath", + }), + }, + }, + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, + }), + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }, + }, + }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_HTTP, + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_HTTP, + Settings: serial.ToTypedMessage(&http.Config{ + Host: []string{"example.com"}, + Path: "/testpath", + }), + }, + }, + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + AllowInsecure: true, + }), + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + common.Must(err) + defer CloseAllServers(servers) + + var errg errgroup.Group + for i := 0; i < 10; i++ { + errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*40)) + } + if err := errg.Wait(); err != nil { + t.Error(err) + } +} + func TestGRPC(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, @@ -516,9 +649,11 @@ func TestGRPC(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -632,9 +767,11 @@ func TestGRPCMultiMode(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -743,9 +880,11 @@ func TestSimpleTLSConnectionPinned(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -845,9 +984,11 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -946,9 +1087,11 @@ func TestUTLSConnectionPinned(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -1049,9 +1192,11 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, diff --git a/testing/scenarios/transport_test.go b/testing/scenarios/transport_test.go index bb16deee..e1db105e 100644 --- a/testing/scenarios/transport_test.go +++ b/testing/scenarios/transport_test.go @@ -1,11 +1,15 @@ package scenarios import ( + "os" + "runtime" "testing" "time" + "github.com/xtls/xray-core/app/log" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/common" + clog "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/serial" @@ -17,9 +21,14 @@ import ( "github.com/xtls/xray-core/proxy/vmess/inbound" "github.com/xtls/xray-core/proxy/vmess/outbound" "github.com/xtls/xray-core/testing/servers/tcp" + "github.com/xtls/xray-core/testing/servers/udp" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/domainsocket" "github.com/xtls/xray-core/transport/internet/headers/http" + "github.com/xtls/xray-core/transport/internet/headers/wechat" + "github.com/xtls/xray-core/transport/internet/quic" tcptransport "github.com/xtls/xray-core/transport/internet/tcp" + "golang.org/x/sync/errgroup" ) func TestHTTPConnectionHeader(t *testing.T) { @@ -41,7 +50,7 @@ func TestHTTPConnectionHeader(t *testing.T) { StreamSettings: &internet.StreamConfig{ TransportSettings: []*internet.TransportConfig{ { - ProtocolName: "tcp", + Protocol: internet.TransportProtocol_TCP, Settings: serial.ToTypedMessage(&tcptransport.Config{ HeaderSettings: serial.ToTypedMessage(&http.Config{}), }), @@ -76,9 +85,11 @@ func TestHTTPConnectionHeader(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -103,7 +114,7 @@ func TestHTTPConnectionHeader(t *testing.T) { StreamSettings: &internet.StreamConfig{ TransportSettings: []*internet.TransportConfig{ { - ProtocolName: "tcp", + Protocol: internet.TransportProtocol_TCP, Settings: serial.ToTypedMessage(&tcptransport.Config{ HeaderSettings: serial.ToTypedMessage(&http.Config{}), }), @@ -123,3 +134,250 @@ func TestHTTPConnectionHeader(t *testing.T) { t.Error(err) } } + +func TestDomainSocket(t *testing.T) { + if runtime.GOOS == "windows" || runtime.GOOS == "android" { + t.Skip("Not supported on windows or android") + return + } + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + const dsPath = "/tmp/ds_scenario" + os.Remove(dsPath) + + userID := protocol.NewID(uuid.New()) + serverPort := tcp.PickPort() + serverConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_DomainSocket, + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_DomainSocket, + Settings: serial.ToTypedMessage(&domainsocket.Config{ + Path: dsPath, + }), + }, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }, + }, + }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_DomainSocket, + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_DomainSocket, + Settings: serial.ToTypedMessage(&domainsocket.Config{ + Path: dsPath, + }), + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + common.Must(err) + defer CloseAllServers(servers) + + if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != nil { + t.Error(err) + } +} + +func TestVMessQuic(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := udp.PickPort() + serverConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: clog.Severity_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + ProtocolName: "quic", + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "quic", + Settings: serial.ToTypedMessage(&quic.Config{ + Header: serial.ToTypedMessage(&wechat.VideoConfig{}), + Security: &protocol.SecurityConfig{ + Type: protocol.SecurityType_NONE, + }, + }), + }, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: clog.Severity_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + ProtocolName: "quic", + TransportSettings: []*internet.TransportConfig{ + { + ProtocolName: "quic", + Settings: serial.ToTypedMessage(&quic.Config{ + Header: serial.ToTypedMessage(&wechat.VideoConfig{}), + Security: &protocol.SecurityConfig{ + Type: protocol.SecurityType_NONE, + }, + }), + }, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + if err != nil { + t.Fatal("Failed to initialize all servers: ", err.Error()) + } + defer CloseAllServers(servers) + + var errg errgroup.Group + for i := 0; i < 10; i++ { + errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) + } + + if err := errg.Wait(); err != nil { + t.Error(err) + } +} diff --git a/testing/scenarios/vless_test.go b/testing/scenarios/vless_test.go deleted file mode 100644 index dcac17ea..00000000 --- a/testing/scenarios/vless_test.go +++ /dev/null @@ -1,511 +0,0 @@ -package scenarios - -import ( - "encoding/base64" - "encoding/hex" - "testing" - "time" - - "github.com/xtls/xray-core/app/log" - "github.com/xtls/xray-core/app/proxyman" - "github.com/xtls/xray-core/common" - clog "github.com/xtls/xray-core/common/log" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/protocol/tls/cert" - "github.com/xtls/xray-core/common/serial" - "github.com/xtls/xray-core/common/uuid" - core "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/proxy/dokodemo" - "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/vless" - "github.com/xtls/xray-core/proxy/vless/inbound" - "github.com/xtls/xray-core/proxy/vless/outbound" - "github.com/xtls/xray-core/testing/servers/tcp" - "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/reality" - transtcp "github.com/xtls/xray-core/transport/internet/tcp" - "github.com/xtls/xray-core/transport/internet/tls" - "golang.org/x/sync/errgroup" -) - -func TestVless(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - Clients: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Vnext: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - }), - }, - }, - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVlessTls(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - StreamSettings: &internet.StreamConfig{ - ProtocolName: "tcp", - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, - }), - }, - }, - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - Clients: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Vnext: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - }), - }, - }, - }, - }, - }), - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ - StreamSettings: &internet.StreamConfig{ - ProtocolName: "tcp", - TransportSettings: []*internet.TransportConfig{ - { - ProtocolName: "tcp", - Settings: serial.ToTypedMessage(&transtcp.Config{}), - }, - }, - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - AllowInsecure: true, - }), - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVlessXtlsVision(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - StreamSettings: &internet.StreamConfig{ - ProtocolName: "tcp", - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, - }), - }, - }, - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - Clients: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - Flow: vless.XRV, - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Vnext: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - Flow: vless.XRV, - }), - }, - }, - }, - }, - }), - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ - StreamSettings: &internet.StreamConfig{ - ProtocolName: "tcp", - TransportSettings: []*internet.TransportConfig{ - { - ProtocolName: "tcp", - Settings: serial.ToTypedMessage(&transtcp.Config{}), - }, - }, - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - AllowInsecure: true, - }), - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} - -func TestVlessXtlsVisionReality(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - userID := protocol.NewID(uuid.New()) - serverPort := tcp.PickPort() - privateKey, _ := base64.RawURLEncoding.DecodeString("aGSYystUbf59_9_6LKRxD27rmSW_-2_nyd9YG_Gwbks") - publicKey, _ := base64.RawURLEncoding.DecodeString("E59WjnvZcQMu7tR7_BgyhycuEdBS-CtKxfImRCdAvFM") - shortIds := make([][]byte, 1) - shortIds[0] = make([]byte, 8) - hex.Decode(shortIds[0], []byte("0123456789abcdef")) - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - StreamSettings: &internet.StreamConfig{ - ProtocolName: "tcp", - SecurityType: serial.GetMessageType(&reality.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&reality.Config{ - Show: true, - Dest: "www.google.com:443", // use google for now, may fail in some region - ServerNames: []string{"www.google.com"}, - PrivateKey: privateKey, - ShortIds: shortIds, - Type: "tcp", - }), - }, - }, - }), - ProxySettings: serial.ToTypedMessage(&inbound.Config{ - Clients: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - Flow: vless.XRV, - }), - }, - }, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&outbound.Config{ - Vnext: []*protocol.ServerEndpoint{ - { - Address: net.NewIPOrDomain(net.LocalHostIP), - Port: uint32(serverPort), - User: []*protocol.User{ - { - Account: serial.ToTypedMessage(&vless.Account{ - Id: userID.String(), - Flow: vless.XRV, - }), - }, - }, - }, - }, - }), - SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ - StreamSettings: &internet.StreamConfig{ - ProtocolName: "tcp", - TransportSettings: []*internet.TransportConfig{ - { - ProtocolName: "tcp", - Settings: serial.ToTypedMessage(&transtcp.Config{}), - }, - }, - SecurityType: serial.GetMessageType(&reality.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&reality.Config{ - Show: true, - Fingerprint: "chrome", - ServerName: "www.google.com", - PublicKey: publicKey, - ShortId: shortIds[0], - SpiderX: "/", - }), - }, - }, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - var errg errgroup.Group - for i := 0; i < 1; i++ { - errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) - } - if err := errg.Wait(); err != nil { - t.Error(err) - } -} diff --git a/testing/scenarios/vmess_test.go b/testing/scenarios/vmess_test.go index dbffe0d6..9f2b0abc 100644 --- a/testing/scenarios/vmess_test.go +++ b/testing/scenarios/vmess_test.go @@ -71,9 +71,11 @@ func TestVMessDynamicPort(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, { @@ -131,9 +133,11 @@ func TestVMessDynamicPort(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -223,9 +227,11 @@ func TestVMessGCM(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -325,9 +331,11 @@ func TestVMessGCMReadv(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -430,9 +438,11 @@ func TestVMessGCMUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, @@ -465,7 +475,7 @@ func TestVMessGCMUDP(t *testing.T) { defer CloseAllServers(servers) var errg errgroup.Group - for i := 0; i < 2; i++ { + for i := 0; i < 10; i++ { errg.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errg.Wait(); err != nil { @@ -529,9 +539,11 @@ func TestVMessChacha20(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -629,9 +641,11 @@ func TestVMessNone(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -695,7 +709,7 @@ func TestVMessKCP(t *testing.T) { PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ @@ -731,9 +745,11 @@ func TestVMessKCP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -759,7 +775,7 @@ func TestVMessKCP(t *testing.T) { }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, }, }), }, @@ -771,8 +787,8 @@ func TestVMessKCP(t *testing.T) { defer CloseAllServers(servers) var errg errgroup.Group - for i := 0; i < 2; i++ { - errg.Go(testTCPConn(clientPort, 1024, time.Minute*2)) + for i := 0; i < 10; i++ { + errg.Go(testTCPConn(clientPort, 10240*1024, time.Minute*2)) } if err := errg.Wait(); err != nil { t.Error(err) @@ -802,10 +818,10 @@ func TestVMessKCPLarge(t *testing.T) { PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, TransportSettings: []*internet.TransportConfig{ { - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, Settings: serial.ToTypedMessage(&kcp.Config{ ReadBuffer: &kcp.ReadBuffer{ Size: 512 * 1024, @@ -857,9 +873,11 @@ func TestVMessKCPLarge(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -885,10 +903,10 @@ func TestVMessKCPLarge(t *testing.T) { }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, TransportSettings: []*internet.TransportConfig{ { - ProtocolName: "mkcp", + Protocol: internet.TransportProtocol_MKCP, Settings: serial.ToTypedMessage(&kcp.Config{ ReadBuffer: &kcp.ReadBuffer{ Size: 512 * 1024, @@ -916,7 +934,7 @@ func TestVMessKCPLarge(t *testing.T) { var errg errgroup.Group for i := 0; i < 2; i++ { - errg.Go(testTCPConn(clientPort, 513*1024, time.Minute*5)) + errg.Go(testTCPConn(clientPort, 10240*1024, time.Minute*5)) } if err := errg.Wait(); err != nil { t.Error(err) @@ -984,9 +1002,11 @@ func TestVMessGCMMux(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -1100,9 +1120,11 @@ func TestVMessGCMMuxUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, { @@ -1111,9 +1133,11 @@ func TestVMessGCMMuxUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(udpDest.Address), - Port: uint32(udpDest.Port), - Networks: []net.Network{net.Network_UDP}, + Address: net.NewIPOrDomain(udpDest.Address), + Port: uint32(udpDest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, }), }, }, @@ -1150,10 +1174,10 @@ func TestVMessGCMMuxUDP(t *testing.T) { servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) - for range "ab" { + for range "abcd" { var errg errgroup.Group - for i := 0; i < 2; i++ { - errg.Go(testTCPConn(clientPort, 1024, time.Second*10)) + for i := 0; i < 16; i++ { + errg.Go(testTCPConn(clientPort, 10240, time.Second*20)) errg.Go(testUDPConn(clientUDPPort, 1024, time.Second*10)) } if err := errg.Wait(); err != nil { @@ -1224,9 +1248,11 @@ func TestVMessZero(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -1323,9 +1349,11 @@ func TestVMessGCMLengthAuth(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, @@ -1427,9 +1455,11 @@ func TestVMessGCMLengthAuthPlusNoTerminationSignal(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, }), }, }, diff --git a/testing/scenarios/wireguard_test.go b/testing/scenarios/wireguard_test.go deleted file mode 100644 index deaee114..00000000 --- a/testing/scenarios/wireguard_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package scenarios - -import ( - "testing" - //"time" - - "github.com/xtls/xray-core/app/log" - "github.com/xtls/xray-core/app/proxyman" - "github.com/xtls/xray-core/common" - clog "github.com/xtls/xray-core/common/log" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/serial" - core "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/infra/conf" - "github.com/xtls/xray-core/proxy/dokodemo" - "github.com/xtls/xray-core/proxy/freedom" - "github.com/xtls/xray-core/proxy/wireguard" - "github.com/xtls/xray-core/testing/servers/tcp" - "github.com/xtls/xray-core/testing/servers/udp" - //"golang.org/x/sync/errgroup" -) - -func TestWireguard(t *testing.T) { - tcpServer := tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - common.Must(err) - defer tcpServer.Close() - - serverPrivate, _ := conf.ParseWireGuardKey("EGs4lTSJPmgELx6YiJAmPR2meWi6bY+e9rTdCipSj10=") - serverPublic, _ := conf.ParseWireGuardKey("osAMIyil18HeZXGGBDC9KpZoM+L2iGyXWVSYivuM9B0=") - clientPrivate, _ := conf.ParseWireGuardKey("CPQSpgxgdQRZa5SUbT3HLv+mmDVHLW5YR/rQlzum/2I=") - clientPublic, _ := conf.ParseWireGuardKey("MmLJ5iHFVVBp7VsB0hxfpQ0wEzAbT2KQnpQpj0+RtBw=") - - serverPort := udp.PickPort() - serverConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&wireguard.DeviceConfig{ - IsClient: false, - NoKernelTun: false, - Endpoint: []string{"10.0.0.1"}, - Mtu: 1420, - SecretKey: serverPrivate, - Peers: []*wireguard.PeerConfig{{ - PublicKey: serverPublic, - AllowedIps: []string{"0.0.0.0/0", "::0/0"}, - }}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }, - }, - } - - clientPort := tcp.PickPort() - clientConfig := &core.Config{ - App: []*serial.TypedMessage{ - serial.ToTypedMessage(&log.Config{ - ErrorLogLevel: clog.Severity_Debug, - ErrorLogType: log.LogType_Console, - }), - }, - Inbound: []*core.InboundHandlerConfig{ - { - ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ - PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, - Listen: net.NewIPOrDomain(net.LocalHostIP), - }), - ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ - Address: net.NewIPOrDomain(dest.Address), - Port: uint32(dest.Port), - Networks: []net.Network{net.Network_TCP}, - }), - }, - }, - Outbound: []*core.OutboundHandlerConfig{ - { - ProxySettings: serial.ToTypedMessage(&wireguard.DeviceConfig{ - IsClient: true, - NoKernelTun: false, - Endpoint: []string{"10.0.0.2"}, - Mtu: 1420, - SecretKey: clientPrivate, - Peers: []*wireguard.PeerConfig{{ - Endpoint: "127.0.0.1:" + serverPort.String(), - PublicKey: clientPublic, - AllowedIps: []string{"0.0.0.0/0", "::0/0"}, - }}, - }), - }, - }, - } - - servers, err := InitializeServerConfigs(serverConfig, clientConfig) - common.Must(err) - defer CloseAllServers(servers) - - // FIXME: for some reason wg server does not receive - - // var errg errgroup.Group - // for i := 0; i < 1; i++ { - // errg.Go(testTCPConn(clientPort, 1024, time.Second*2)) - // } - // if err := errg.Wait(); err != nil { - // t.Error(err) - // } -} diff --git a/transport/global/config.go b/transport/global/config.go new file mode 100644 index 00000000..b6dd8e2a --- /dev/null +++ b/transport/global/config.go @@ -0,0 +1,13 @@ +package global + +import ( + "github.com/xtls/xray-core/transport/internet" +) + +// Apply applies this Config. +func (c *Config) Apply() error { + if c == nil { + return nil + } + return internet.ApplyGlobalTransportSettings(c.TransportSettings) +} diff --git a/transport/global/config.pb.go b/transport/global/config.pb.go new file mode 100644 index 00000000..eb0fcd0d --- /dev/null +++ b/transport/global/config.pb.go @@ -0,0 +1,162 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: transport/global/config.proto + +package global + +import ( + internet "github.com/xtls/xray-core/transport/internet" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Global transport settings. This affects all type of connections that go +// through Xray. Deprecated. Use each settings in StreamConfig. +// +// Deprecated: Do not use. +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TransportSettings []*internet.TransportConfig `protobuf:"bytes,1,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_transport_global_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_transport_global_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Config.ProtoReflect.Descriptor instead. +func (*Config) Descriptor() ([]byte, []int) { + return file_transport_global_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetTransportSettings() []*internet.TransportConfig { + if x != nil { + return x.TransportSettings + } + return nil +} + +var File_transport_global_config_proto protoreflect.FileDescriptor + +var file_transport_global_config_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x1a, + 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x65, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x57, 0x0a, 0x12, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 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, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x3a, 0x02, 0x18, 0x01, 0x42, 0x61, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x01, 0x5a, 0x2a, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0xaa, 0x02, 0x15, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_transport_global_config_proto_rawDescOnce sync.Once + file_transport_global_config_proto_rawDescData = file_transport_global_config_proto_rawDesc +) + +func file_transport_global_config_proto_rawDescGZIP() []byte { + file_transport_global_config_proto_rawDescOnce.Do(func() { + file_transport_global_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_global_config_proto_rawDescData) + }) + return file_transport_global_config_proto_rawDescData +} + +var file_transport_global_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_global_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.transport.Config + (*internet.TransportConfig)(nil), // 1: xray.transport.internet.TransportConfig +} +var file_transport_global_config_proto_depIdxs = []int32{ + 1, // 0: xray.transport.Config.transport_settings:type_name -> xray.transport.internet.TransportConfig + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_transport_global_config_proto_init() } +func file_transport_global_config_proto_init() { + if File_transport_global_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_transport_global_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_transport_global_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_global_config_proto_goTypes, + DependencyIndexes: file_transport_global_config_proto_depIdxs, + MessageInfos: file_transport_global_config_proto_msgTypes, + }.Build() + File_transport_global_config_proto = out.File + file_transport_global_config_proto_rawDesc = nil + file_transport_global_config_proto_goTypes = nil + file_transport_global_config_proto_depIdxs = nil +} diff --git a/transport/global/config.proto b/transport/global/config.proto new file mode 100644 index 00000000..367036ec --- /dev/null +++ b/transport/global/config.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package xray.transport; +option csharp_namespace = "Xray.Transport.Global"; +option go_package = "github.com/xtls/xray-core/transport/global"; +option java_package = "com.xray.transport.global"; +option java_multiple_files = true; + +import "transport/internet/config.proto"; + +// Global transport settings. This affects all type of connections that go +// through Xray. Deprecated. Use each settings in StreamConfig. +message Config { + option deprecated = true; + repeated xray.transport.internet.TransportConfig transport_settings = 1; +} diff --git a/transport/internet/browser_dialer/dialer.go b/transport/internet/browser_dialer/dialer.go deleted file mode 100644 index 1991284d..00000000 --- a/transport/internet/browser_dialer/dialer.go +++ /dev/null @@ -1,179 +0,0 @@ -package browser_dialer - -import ( - "bytes" - "context" - _ "embed" - "encoding/base64" - "encoding/json" - "net/http" - "time" - - "github.com/gorilla/websocket" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/platform" - "github.com/xtls/xray-core/common/uuid" -) - -//go:embed dialer.html -var webpage []byte - -type task struct { - Method string `json:"method"` - URL string `json:"url"` - Extra any `json:"extra,omitempty"` -} - -var conns chan *websocket.Conn - -var upgrader = &websocket.Upgrader{ - ReadBufferSize: 0, - WriteBufferSize: 0, - HandshakeTimeout: time.Second * 4, - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - -func init() { - addr := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" }) - if addr != "" { - token := uuid.New() - csrfToken := token.String() - webpage = bytes.ReplaceAll(webpage, []byte("csrfToken"), []byte(csrfToken)) - conns = make(chan *websocket.Conn, 256) - go http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/websocket" { - if r.URL.Query().Get("token") == csrfToken { - if conn, err := upgrader.Upgrade(w, r, nil); err == nil { - conns <- conn - } else { - errors.LogError(context.Background(), "Browser dialer http upgrade unexpected error") - } - } - } else { - w.Write(webpage) - } - })) - } -} - -func HasBrowserDialer() bool { - return conns != nil -} - -type webSocketExtra struct { - Protocol string `json:"protocol,omitempty"` -} - -func DialWS(uri string, ed []byte) (*websocket.Conn, error) { - task := task{ - Method: "WS", - URL: uri, - } - - if ed != nil { - task.Extra = webSocketExtra{ - Protocol: base64.RawURLEncoding.EncodeToString(ed), - } - } - - return dialTask(task) -} - -type httpExtra struct { - Referrer string `json:"referrer,omitempty"` - Headers map[string]string `json:"headers,omitempty"` -} - -func httpExtraFromHeaders(headers http.Header) *httpExtra { - if len(headers) == 0 { - return nil - } - - extra := httpExtra{} - if referrer := headers.Get("Referer"); referrer != "" { - extra.Referrer = referrer - headers.Del("Referer") - } - - if len(headers) > 0 { - extra.Headers = make(map[string]string) - for header := range headers { - extra.Headers[header] = headers.Get(header) - } - } - - return &extra -} - -func DialGet(uri string, headers http.Header) (*websocket.Conn, error) { - task := task{ - Method: "GET", - URL: uri, - Extra: httpExtraFromHeaders(headers), - } - - return dialTask(task) -} - -func DialPost(uri string, headers http.Header, payload []byte) error { - task := task{ - Method: "POST", - URL: uri, - Extra: httpExtraFromHeaders(headers), - } - - conn, err := dialTask(task) - if err != nil { - return err - } - - err = conn.WriteMessage(websocket.BinaryMessage, payload) - if err != nil { - return err - } - - err = CheckOK(conn) - if err != nil { - return err - } - - conn.Close() - return nil -} - -func dialTask(task task) (*websocket.Conn, error) { - data, err := json.Marshal(task) - if err != nil { - return nil, err - } - - var conn *websocket.Conn - for { - conn = <-conns - if conn.WriteMessage(websocket.TextMessage, data) != nil { - conn.Close() - } else { - break - } - } - err = CheckOK(conn) - if err != nil { - return nil, err - } - - return conn, nil -} - -func CheckOK(conn *websocket.Conn) error { - if _, p, err := conn.ReadMessage(); err != nil { - conn.Close() - return err - } else if s := string(p); s != "ok" { - conn.Close() - return errors.New(s) - } - - return nil -} diff --git a/transport/internet/browser_dialer/dialer.html b/transport/internet/browser_dialer/dialer.html deleted file mode 100644 index c62135ae..00000000 --- a/transport/internet/browser_dialer/dialer.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - Browser Dialer - - - - - diff --git a/transport/internet/config.go b/transport/internet/config.go index 8608b872..a7ff96c6 100644 --- a/transport/internet/config.go +++ b/transport/internet/config.go @@ -1,47 +1,50 @@ package internet import ( - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/features" ) type ConfigCreator func() interface{} var ( globalTransportConfigCreatorCache = make(map[string]ConfigCreator) + globalTransportSettings []*TransportConfig ) -var strategy = [][]byte{ - // name strategy, prefer, fallback - {0, 0, 0}, // AsIs none, /, / - {1, 0, 0}, // UseIP use, both, none - {1, 4, 0}, // UseIPv4 use, 4, none - {1, 6, 0}, // UseIPv6 use, 6, none - {1, 4, 6}, // UseIPv4v6 use, 4, 6 - {1, 6, 4}, // UseIPv6v4 use, 6, 4 - {2, 0, 0}, // ForceIP force, both, none - {2, 4, 0}, // ForceIPv4 force, 4, none - {2, 6, 0}, // ForceIPv6 force, 6, none - {2, 4, 6}, // ForceIPv4v6 force, 4, 6 - {2, 6, 4}, // ForceIPv6v4 force, 6, 4 -} - const unknownProtocol = "unknown" +func transportProtocolToString(protocol TransportProtocol) string { + switch protocol { + case TransportProtocol_TCP: + return "tcp" + case TransportProtocol_UDP: + return "udp" + case TransportProtocol_HTTP: + return "http" + case TransportProtocol_MKCP: + return "mkcp" + case TransportProtocol_WebSocket: + return "websocket" + case TransportProtocol_DomainSocket: + return "domainsocket" + default: + return unknownProtocol + } +} + func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error { if _, found := globalTransportConfigCreatorCache[name]; found { - return errors.New("protocol ", name, " is already registered").AtError() + return newError("protocol ", name, " is already registered").AtError() } globalTransportConfigCreatorCache[name] = creator return nil } -// Note: Each new transport needs to add init() func in transport/internet/xxx/config.go -// Otherwise, it will cause #3244 func CreateTransportConfig(name string) (interface{}, error) { creator, ok := globalTransportConfigCreatorCache[name] if !ok { - return nil, errors.New("unknown transport protocol: ", name) + return nil, newError("unknown transport protocol: ", name) } return creator(), nil } @@ -51,15 +54,23 @@ func (c *TransportConfig) GetTypedSettings() (interface{}, error) { } func (c *TransportConfig) GetUnifiedProtocolName() string { - return c.ProtocolName + if len(c.ProtocolName) > 0 { + return c.ProtocolName + } + + return transportProtocolToString(c.Protocol) } func (c *StreamConfig) GetEffectiveProtocol() string { - if c == nil || len(c.ProtocolName) == 0 { + if c == nil { return "tcp" } - return c.ProtocolName + if len(c.ProtocolName) > 0 { + return c.ProtocolName + } + + return transportProtocolToString(c.Protocol) } func (c *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) { @@ -76,6 +87,12 @@ func (c *StreamConfig) GetTransportSettingsFor(protocol string) (interface{}, er } } + for _, settings := range globalTransportSettings { + if settings.GetUnifiedProtocolName() == protocol { + return settings.GetTypedSettings() + } + } + return CreateTransportConfig(protocol) } @@ -92,6 +109,12 @@ func (c *StreamConfig) HasSecuritySettings() bool { return len(c.SecurityType) > 0 } +func ApplyGlobalTransportSettings(settings []*TransportConfig) error { + features.PrintDeprecatedFeatureWarning("global transport settings") + globalTransportSettings = settings + return nil +} + func (c *ProxyConfig) HasTag() bool { return c != nil && len(c.Tag) > 0 } @@ -99,31 +122,3 @@ func (c *ProxyConfig) HasTag() bool { func (m SocketConfig_TProxyMode) IsEnabled() bool { return m != SocketConfig_Off } - -func (s DomainStrategy) hasStrategy() bool { - return strategy[s][0] != 0 -} - -func (s DomainStrategy) forceIP() bool { - return strategy[s][0] == 2 -} - -func (s DomainStrategy) preferIP4() bool { - return strategy[s][1] == 4 || strategy[s][1] == 0 -} - -func (s DomainStrategy) preferIP6() bool { - return strategy[s][1] == 6 || strategy[s][1] == 0 -} - -func (s DomainStrategy) hasFallback() bool { - return strategy[s][2] != 0 -} - -func (s DomainStrategy) fallbackIP4() bool { - return strategy[s][2] == 4 -} - -func (s DomainStrategy) fallbackIP6() bool { - return strategy[s][2] == 6 -} diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go index f974926e..90dcd786 100644 --- a/transport/internet/config.pb.go +++ b/transport/internet/config.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v4.22.0 // source: transport/internet/config.proto package internet import ( - net "github.com/xtls/xray-core/common/net" serial "github.com/xtls/xray-core/common/serial" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -22,49 +21,86 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type TransportProtocol int32 + +const ( + TransportProtocol_TCP TransportProtocol = 0 + TransportProtocol_UDP TransportProtocol = 1 + TransportProtocol_MKCP TransportProtocol = 2 + TransportProtocol_WebSocket TransportProtocol = 3 + TransportProtocol_HTTP TransportProtocol = 4 + TransportProtocol_DomainSocket TransportProtocol = 5 +) + +// Enum value maps for TransportProtocol. +var ( + TransportProtocol_name = map[int32]string{ + 0: "TCP", + 1: "UDP", + 2: "MKCP", + 3: "WebSocket", + 4: "HTTP", + 5: "DomainSocket", + } + TransportProtocol_value = map[string]int32{ + "TCP": 0, + "UDP": 1, + "MKCP": 2, + "WebSocket": 3, + "HTTP": 4, + "DomainSocket": 5, + } +) + +func (x TransportProtocol) Enum() *TransportProtocol { + p := new(TransportProtocol) + *p = x + return p +} + +func (x TransportProtocol) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TransportProtocol) Descriptor() protoreflect.EnumDescriptor { + return file_transport_internet_config_proto_enumTypes[0].Descriptor() +} + +func (TransportProtocol) Type() protoreflect.EnumType { + return &file_transport_internet_config_proto_enumTypes[0] +} + +func (x TransportProtocol) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TransportProtocol.Descriptor instead. +func (TransportProtocol) EnumDescriptor() ([]byte, []int) { + return file_transport_internet_config_proto_rawDescGZIP(), []int{0} +} + type DomainStrategy int32 const ( - DomainStrategy_AS_IS DomainStrategy = 0 - DomainStrategy_USE_IP DomainStrategy = 1 - DomainStrategy_USE_IP4 DomainStrategy = 2 - DomainStrategy_USE_IP6 DomainStrategy = 3 - DomainStrategy_USE_IP46 DomainStrategy = 4 - DomainStrategy_USE_IP64 DomainStrategy = 5 - DomainStrategy_FORCE_IP DomainStrategy = 6 - DomainStrategy_FORCE_IP4 DomainStrategy = 7 - DomainStrategy_FORCE_IP6 DomainStrategy = 8 - DomainStrategy_FORCE_IP46 DomainStrategy = 9 - DomainStrategy_FORCE_IP64 DomainStrategy = 10 + DomainStrategy_AS_IS DomainStrategy = 0 + DomainStrategy_USE_IP DomainStrategy = 1 + DomainStrategy_USE_IP4 DomainStrategy = 2 + DomainStrategy_USE_IP6 DomainStrategy = 3 ) // Enum value maps for DomainStrategy. var ( DomainStrategy_name = map[int32]string{ - 0: "AS_IS", - 1: "USE_IP", - 2: "USE_IP4", - 3: "USE_IP6", - 4: "USE_IP46", - 5: "USE_IP64", - 6: "FORCE_IP", - 7: "FORCE_IP4", - 8: "FORCE_IP6", - 9: "FORCE_IP46", - 10: "FORCE_IP64", + 0: "AS_IS", + 1: "USE_IP", + 2: "USE_IP4", + 3: "USE_IP6", } DomainStrategy_value = map[string]int32{ - "AS_IS": 0, - "USE_IP": 1, - "USE_IP4": 2, - "USE_IP6": 3, - "USE_IP46": 4, - "USE_IP64": 5, - "FORCE_IP": 6, - "FORCE_IP4": 7, - "FORCE_IP6": 8, - "FORCE_IP46": 9, - "FORCE_IP64": 10, + "AS_IS": 0, + "USE_IP": 1, + "USE_IP4": 2, + "USE_IP6": 3, } ) @@ -79,11 +115,11 @@ func (x DomainStrategy) String() string { } func (DomainStrategy) Descriptor() protoreflect.EnumDescriptor { - return file_transport_internet_config_proto_enumTypes[0].Descriptor() + return file_transport_internet_config_proto_enumTypes[1].Descriptor() } func (DomainStrategy) Type() protoreflect.EnumType { - return &file_transport_internet_config_proto_enumTypes[0] + return &file_transport_internet_config_proto_enumTypes[1] } func (x DomainStrategy) Number() protoreflect.EnumNumber { @@ -92,67 +128,6 @@ func (x DomainStrategy) Number() protoreflect.EnumNumber { // Deprecated: Use DomainStrategy.Descriptor instead. func (DomainStrategy) EnumDescriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{0} -} - -type AddressPortStrategy int32 - -const ( - AddressPortStrategy_None AddressPortStrategy = 0 - AddressPortStrategy_SrvPortOnly AddressPortStrategy = 1 - AddressPortStrategy_SrvAddressOnly AddressPortStrategy = 2 - AddressPortStrategy_SrvPortAndAddress AddressPortStrategy = 3 - AddressPortStrategy_TxtPortOnly AddressPortStrategy = 4 - AddressPortStrategy_TxtAddressOnly AddressPortStrategy = 5 - AddressPortStrategy_TxtPortAndAddress AddressPortStrategy = 6 -) - -// Enum value maps for AddressPortStrategy. -var ( - AddressPortStrategy_name = map[int32]string{ - 0: "None", - 1: "SrvPortOnly", - 2: "SrvAddressOnly", - 3: "SrvPortAndAddress", - 4: "TxtPortOnly", - 5: "TxtAddressOnly", - 6: "TxtPortAndAddress", - } - AddressPortStrategy_value = map[string]int32{ - "None": 0, - "SrvPortOnly": 1, - "SrvAddressOnly": 2, - "SrvPortAndAddress": 3, - "TxtPortOnly": 4, - "TxtAddressOnly": 5, - "TxtPortAndAddress": 6, - } -) - -func (x AddressPortStrategy) Enum() *AddressPortStrategy { - p := new(AddressPortStrategy) - *p = x - return p -} - -func (x AddressPortStrategy) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (AddressPortStrategy) Descriptor() protoreflect.EnumDescriptor { - return file_transport_internet_config_proto_enumTypes[1].Descriptor() -} - -func (AddressPortStrategy) Type() protoreflect.EnumType { - return &file_transport_internet_config_proto_enumTypes[1] -} - -func (x AddressPortStrategy) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use AddressPortStrategy.Descriptor instead. -func (AddressPortStrategy) EnumDescriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{1} } @@ -205,7 +180,7 @@ func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber { // Deprecated: Use SocketConfig_TProxyMode.Descriptor instead. func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{4, 0} + return file_transport_internet_config_proto_rawDescGZIP(), []int{3, 0} } type TransportConfig struct { @@ -213,17 +188,24 @@ type TransportConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Transport protocol name. + // Type of network that this settings supports. + // Deprecated. Use the string form below. + // + // Deprecated: Do not use. + Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=xray.transport.internet.TransportProtocol" json:"protocol,omitempty"` + // Type of network that this settings supports. ProtocolName string `protobuf:"bytes,3,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"` - // Specific transport protocol settings. + // Specific settings. Must be of the transports. Settings *serial.TypedMessage `protobuf:"bytes,2,opt,name=settings,proto3" json:"settings,omitempty"` } func (x *TransportConfig) Reset() { *x = TransportConfig{} - mi := &file_transport_internet_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *TransportConfig) String() string { @@ -234,7 +216,7 @@ func (*TransportConfig) ProtoMessage() {} func (x *TransportConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -249,6 +231,14 @@ func (*TransportConfig) Descriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{0} } +// Deprecated: Do not use. +func (x *TransportConfig) GetProtocol() TransportProtocol { + if x != nil { + return x.Protocol + } + return TransportProtocol_TCP +} + func (x *TransportConfig) GetProtocolName() string { if x != nil { return x.ProtocolName @@ -268,23 +258,27 @@ type StreamConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address *net.IPOrDomain `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty"` - Port uint32 `protobuf:"varint,9,opt,name=port,proto3" json:"port,omitempty"` + // Effective network. Deprecated. Use the string form below. + // + // Deprecated: Do not use. + Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=xray.transport.internet.TransportProtocol" json:"protocol,omitempty"` // Effective network. ProtocolName string `protobuf:"bytes,5,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"` TransportSettings []*TransportConfig `protobuf:"bytes,2,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"` // Type of security. Must be a message name of the settings proto. SecurityType string `protobuf:"bytes,3,opt,name=security_type,json=securityType,proto3" json:"security_type,omitempty"` - // Transport security settings. They can be either TLS or REALITY. + // Settings for transport security. For now the only choice is TLS. SecuritySettings []*serial.TypedMessage `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` SocketSettings *SocketConfig `protobuf:"bytes,6,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"` } func (x *StreamConfig) Reset() { *x = StreamConfig{} - mi := &file_transport_internet_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *StreamConfig) String() string { @@ -295,7 +289,7 @@ func (*StreamConfig) ProtoMessage() {} func (x *StreamConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -310,18 +304,12 @@ func (*StreamConfig) Descriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{1} } -func (x *StreamConfig) GetAddress() *net.IPOrDomain { +// Deprecated: Do not use. +func (x *StreamConfig) GetProtocol() TransportProtocol { if x != nil { - return x.Address + return x.Protocol } - return nil -} - -func (x *StreamConfig) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 + return TransportProtocol_TCP } func (x *StreamConfig) GetProtocolName() string { @@ -370,9 +358,11 @@ type ProxyConfig struct { func (x *ProxyConfig) Reset() { *x = ProxyConfig{} - mi := &file_transport_internet_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ProxyConfig) String() string { @@ -383,7 +373,7 @@ func (*ProxyConfig) ProtoMessage() {} func (x *ProxyConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -412,91 +402,6 @@ func (x *ProxyConfig) GetTransportLayerProxy() bool { return false } -type CustomSockopt struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - System string `protobuf:"bytes,1,opt,name=system,proto3" json:"system,omitempty"` - Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"` - Level string `protobuf:"bytes,3,opt,name=level,proto3" json:"level,omitempty"` - Opt string `protobuf:"bytes,4,opt,name=opt,proto3" json:"opt,omitempty"` - Value string `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"` - Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"` -} - -func (x *CustomSockopt) Reset() { - *x = CustomSockopt{} - mi := &file_transport_internet_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CustomSockopt) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CustomSockopt) ProtoMessage() {} - -func (x *CustomSockopt) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_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 CustomSockopt.ProtoReflect.Descriptor instead. -func (*CustomSockopt) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{3} -} - -func (x *CustomSockopt) GetSystem() string { - if x != nil { - return x.System - } - return "" -} - -func (x *CustomSockopt) GetNetwork() string { - if x != nil { - return x.Network - } - return "" -} - -func (x *CustomSockopt) GetLevel() string { - if x != nil { - return x.Level - } - return "" -} - -func (x *CustomSockopt) GetOpt() string { - if x != nil { - return x.Opt - } - return "" -} - -func (x *CustomSockopt) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -func (x *CustomSockopt) GetType() string { - if x != nil { - return x.Type - } - return "" -} - // SocketConfig is options to be applied on network sockets. type SocketConfig struct { state protoimpl.MessageState @@ -511,32 +416,27 @@ type SocketConfig struct { Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=xray.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"` // ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket // option. This option is for UDP only. - ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"` - BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"` - BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"` - AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` - DomainStrategy DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"` - DialerProxy string `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"` - TcpKeepAliveInterval int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"` - TcpKeepAliveIdle int32 `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"` - TcpCongestion string `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"` - Interface string `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"` - V6Only bool `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"` - TcpWindowClamp int32 `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"` - TcpUserTimeout int32 `protobuf:"varint,16,opt,name=tcp_user_timeout,json=tcpUserTimeout,proto3" json:"tcp_user_timeout,omitempty"` - TcpMaxSeg int32 `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"` - Penetrate bool `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"` - TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"` - CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"` - AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"` - HappyEyeballs *HappyEyeballsConfig `protobuf:"bytes,22,opt,name=happy_eyeballs,json=happyEyeballs,proto3" json:"happy_eyeballs,omitempty"` + ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"` + BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"` + BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"` + AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` + DomainStrategy DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"` + DialerProxy string `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"` + TcpKeepAliveInterval int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"` + TcpKeepAliveIdle int32 `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"` + TcpCongestion string `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"` + Interface string `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"` + V6Only bool `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"` + TcpWindowClamp int32 `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"` } func (x *SocketConfig) Reset() { *x = SocketConfig{} - mi := &file_transport_internet_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *SocketConfig) String() string { @@ -546,8 +446,8 @@ func (x *SocketConfig) String() string { func (*SocketConfig) ProtoMessage() {} func (x *SocketConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_config_proto_msgTypes[4] - if x != nil { + mi := &file_transport_internet_config_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -559,7 +459,7 @@ func (x *SocketConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead. func (*SocketConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{4} + return file_transport_internet_config_proto_rawDescGZIP(), []int{3} } func (x *SocketConfig) GetMark() int32 { @@ -667,124 +567,6 @@ func (x *SocketConfig) GetTcpWindowClamp() int32 { return 0 } -func (x *SocketConfig) GetTcpUserTimeout() int32 { - if x != nil { - return x.TcpUserTimeout - } - return 0 -} - -func (x *SocketConfig) GetTcpMaxSeg() int32 { - if x != nil { - return x.TcpMaxSeg - } - return 0 -} - -func (x *SocketConfig) GetPenetrate() bool { - if x != nil { - return x.Penetrate - } - return false -} - -func (x *SocketConfig) GetTcpMptcp() bool { - if x != nil { - return x.TcpMptcp - } - return false -} - -func (x *SocketConfig) GetCustomSockopt() []*CustomSockopt { - if x != nil { - return x.CustomSockopt - } - return nil -} - -func (x *SocketConfig) GetAddressPortStrategy() AddressPortStrategy { - if x != nil { - return x.AddressPortStrategy - } - return AddressPortStrategy_None -} - -func (x *SocketConfig) GetHappyEyeballs() *HappyEyeballsConfig { - if x != nil { - return x.HappyEyeballs - } - return nil -} - -type HappyEyeballsConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PrioritizeIpv6 bool `protobuf:"varint,1,opt,name=prioritize_ipv6,json=prioritizeIpv6,proto3" json:"prioritize_ipv6,omitempty"` - Interleave uint32 `protobuf:"varint,2,opt,name=interleave,proto3" json:"interleave,omitempty"` - TryDelayMs uint64 `protobuf:"varint,3,opt,name=try_delayMs,json=tryDelayMs,proto3" json:"try_delayMs,omitempty"` - MaxConcurrentTry uint32 `protobuf:"varint,4,opt,name=max_concurrent_try,json=maxConcurrentTry,proto3" json:"max_concurrent_try,omitempty"` -} - -func (x *HappyEyeballsConfig) Reset() { - *x = HappyEyeballsConfig{} - mi := &file_transport_internet_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HappyEyeballsConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HappyEyeballsConfig) ProtoMessage() {} - -func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_config_proto_msgTypes[5] - 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 HappyEyeballsConfig.ProtoReflect.Descriptor instead. -func (*HappyEyeballsConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{5} -} - -func (x *HappyEyeballsConfig) GetPrioritizeIpv6() bool { - if x != nil { - return x.PrioritizeIpv6 - } - return false -} - -func (x *HappyEyeballsConfig) GetInterleave() uint32 { - if x != nil { - return x.Interleave - } - return 0 -} - -func (x *HappyEyeballsConfig) GetTryDelayMs() uint64 { - if x != nil { - return x.TryDelayMs - } - return 0 -} - -func (x *HappyEyeballsConfig) GetMaxConcurrentTry() uint32 { - if x != nil { - return x.MaxConcurrentTry - } - return 0 -} - var File_transport_internet_config_proto protoreflect.FileDescriptor var file_transport_internet_config_proto_rawDesc = []byte{ @@ -793,164 +575,112 @@ var file_transport_internet_config_proto_rawDesc = []byte{ 0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x74, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x3c, 0x0a, 0x08, 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, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x9b, 0x03, - 0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 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, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x57, - 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 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, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4d, 0x0a, 0x11, - 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x18, 0x04, 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, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4e, 0x0a, 0x0f, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, - 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, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x51, 0x0a, 0x0b, 0x50, - 0x72, 0x6f, 0x78, 0x79, 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, 0x30, 0x0a, 0x13, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x93, - 0x01, 0x0a, 0x0d, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x70, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x22, 0xd2, 0x08, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 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, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 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, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, - 0x62, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, - 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, - 0x62, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, 0x0a, 0x0f, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, - 0x08, 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, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x21, - 0x0a, 0x0c, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x12, 0x35, 0x0a, 0x17, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, - 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x14, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70, 0x5f, - 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, - 0x69, 0x76, 0x65, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, - 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x74, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, - 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x76, 0x36, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x36, - 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x77, 0x69, 0x6e, 0x64, - 0x6f, 0x77, 0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, - 0x74, 0x63, 0x70, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x43, 0x6c, 0x61, 0x6d, 0x70, 0x12, 0x28, - 0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x63, 0x70, 0x55, 0x73, 0x65, - 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x74, 0x63, 0x70, 0x5f, - 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, - 0x63, 0x70, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x6e, 0x65, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x6e, - 0x65, 0x74, 0x72, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x6d, 0x70, - 0x74, 0x63, 0x70, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x63, 0x70, 0x4d, 0x70, - 0x74, 0x63, 0x70, 0x12, 0x4c, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, - 0x6b, 0x6f, 0x70, 0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 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, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, - 0x70, 0x74, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, - 0x74, 0x12, 0x60, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x6f, 0x72, - 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x2c, 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, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x12, 0x53, 0x0a, 0x0e, 0x68, 0x61, 0x70, 0x70, 0x79, 0x5f, 0x65, 0x79, 0x65, - 0x62, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 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, 0x48, 0x61, 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61, - 0x6c, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x68, 0x61, 0x70, 0x70, 0x79, - 0x45, 0x79, 0x65, 0x62, 0x61, 0x6c, 0x6c, 0x73, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, - 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, - 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x22, 0xad, 0x01, 0x0a, 0x13, 0x48, 0x61, - 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61, 0x6c, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x5f, - 0x69, 0x70, 0x76, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x72, 0x69, 0x6f, - 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x49, 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, - 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6d, - 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x72, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x79, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, - 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, - 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, - 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, - 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, - 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, - 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, - 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, - 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, - 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, - 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, - 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, - 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, - 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, - 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, - 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 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, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, - 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0, 0x01, + 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2a, 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, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x23, 0x0a, + 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x08, 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, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x22, 0x9c, 0x03, 0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2a, 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, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x23, 0x0a, + 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x57, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, + 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, + 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, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, + 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 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, 0x10, 0x73, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0x4e, 0x0a, 0x0f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x06, 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, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x0e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, + 0x51, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, 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, 0x30, 0x0a, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, + 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x22, 0xc8, 0x05, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 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, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 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, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69, + 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e, + 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x69, + 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, 0x0a, 0x0f, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08, 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, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x21, 0x0a, 0x0c, + 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, + 0x35, 0x0a, 0x17, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, + 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x14, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, + 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x10, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, + 0x65, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, + 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, + 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x36, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x36, 0x6f, 0x6e, + 0x6c, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x63, + 0x70, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x43, 0x6c, 0x61, 0x6d, 0x70, 0x22, 0x2f, 0x0a, 0x0a, + 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, + 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, + 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x5a, 0x0a, + 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, + 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, + 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, + 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x05, 0x2a, 0x41, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, + 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, + 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x42, 0x67, 0x0a, 0x1b, + 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, + 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -966,36 +696,31 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte { } var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_transport_internet_config_proto_goTypes = []any{ - (DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy - (AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy +var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_transport_internet_config_proto_goTypes = []interface{}{ + (TransportProtocol)(0), // 0: xray.transport.internet.TransportProtocol + (DomainStrategy)(0), // 1: xray.transport.internet.DomainStrategy (SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode (*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig (*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig (*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig - (*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt - (*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig - (*HappyEyeballsConfig)(nil), // 8: xray.transport.internet.HappyEyeballsConfig - (*serial.TypedMessage)(nil), // 9: xray.common.serial.TypedMessage - (*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain + (*SocketConfig)(nil), // 6: xray.transport.internet.SocketConfig + (*serial.TypedMessage)(nil), // 7: xray.common.serial.TypedMessage } var file_transport_internet_config_proto_depIdxs = []int32{ - 9, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage - 10, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain - 3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig - 9, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage - 7, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig - 2, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode - 0, // 6: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy - 6, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt - 1, // 8: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy - 8, // 9: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig - 10, // [10:10] is the sub-list for method output_type - 10, // [10:10] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 0, // 0: xray.transport.internet.TransportConfig.protocol:type_name -> xray.transport.internet.TransportProtocol + 7, // 1: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage + 0, // 2: xray.transport.internet.StreamConfig.protocol:type_name -> xray.transport.internet.TransportProtocol + 3, // 3: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig + 7, // 4: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage + 6, // 5: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig + 2, // 6: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode + 1, // 7: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 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_transport_internet_config_proto_init() } @@ -1003,13 +728,63 @@ func file_transport_internet_config_proto_init() { if File_transport_internet_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransportConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProxyConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SocketConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_transport_internet_config_proto_rawDesc, NumEnums: 3, - NumMessages: 6, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/transport/internet/config.proto b/transport/internet/config.proto index a40f0938..574d698e 100644 --- a/transport/internet/config.proto +++ b/transport/internet/config.proto @@ -7,43 +7,38 @@ option java_package = "com.xray.transport.internet"; option java_multiple_files = true; import "common/serial/typed_message.proto"; -import "common/net/address.proto"; + +enum TransportProtocol { + TCP = 0; + UDP = 1; + MKCP = 2; + WebSocket = 3; + HTTP = 4; + DomainSocket = 5; +} enum DomainStrategy { AS_IS = 0; USE_IP = 1; USE_IP4 = 2; USE_IP6 = 3; - USE_IP46 = 4; - USE_IP64 = 5; - FORCE_IP = 6; - FORCE_IP4 = 7; - FORCE_IP6 = 8; - FORCE_IP46 = 9; - FORCE_IP64 = 10; -} - -enum AddressPortStrategy { - None = 0; - SrvPortOnly = 1; - SrvAddressOnly = 2; - SrvPortAndAddress = 3; - TxtPortOnly = 4; - TxtAddressOnly = 5; - TxtPortAndAddress = 6; } message TransportConfig { - // Transport protocol name. + // Type of network that this settings supports. + // Deprecated. Use the string form below. + TransportProtocol protocol = 1 [ deprecated = true ]; + + // Type of network that this settings supports. string protocol_name = 3; - // Specific transport protocol settings. + // Specific settings. Must be of the transports. xray.common.serial.TypedMessage settings = 2; } message StreamConfig { - xray.common.net.IPOrDomain address = 8; - uint32 port = 9; + // Effective network. Deprecated. Use the string form below. + TransportProtocol protocol = 1 [ deprecated = true ]; // Effective network. string protocol_name = 5; @@ -53,7 +48,7 @@ message StreamConfig { // Type of security. Must be a message name of the settings proto. string security_type = 3; - // Transport security settings. They can be either TLS or REALITY. + // Settings for transport security. For now the only choice is TLS. repeated xray.common.serial.TypedMessage security_settings = 4; SocketConfig socket_settings = 6; @@ -64,15 +59,6 @@ message ProxyConfig { bool transportLayerProxy = 2; } -message CustomSockopt { - string system = 1; - string network = 2; - string level = 3; - string opt = 4; - string value = 5; - string type = 6; -} - // SocketConfig is options to be applied on network sockets. message SocketConfig { // Mark of the connection. If non-zero, the value will be set to SO_MARK. @@ -115,28 +101,7 @@ message SocketConfig { string interface = 13; - bool v6only = 14; + bool v6only = 14; int32 tcp_window_clamp = 15; - - int32 tcp_user_timeout = 16; - - int32 tcp_max_seg = 17; - - bool penetrate = 18; - - bool tcp_mptcp = 19; - - repeated CustomSockopt customSockopt = 20; - - AddressPortStrategy address_port_strategy = 21; - - HappyEyeballsConfig happy_eyeballs = 22; -} - -message HappyEyeballsConfig { - bool prioritize_ipv6 = 1; - uint32 interleave = 2; - uint64 try_delayMs = 3; - uint32 max_concurrent_try = 4; } diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index 5e403f67..d178bdcd 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -2,13 +2,9 @@ package internet import ( "context" - "fmt" - gonet "net" - "strings" "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/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/session" @@ -26,9 +22,6 @@ type Dialer interface { // Address returns the address used by this Dialer. Maybe nil if not known. Address() net.Address - - // DestIpAddress returns the ip of proxy server. It is useful in case of Android client, which prepare an IP before proxy connection is established - DestIpAddress() net.IP } // dialFunc is an interface to dial network connection to a specific destination. @@ -39,7 +32,7 @@ var transportDialerCache = make(map[string]dialFunc) // RegisterTransportDialer registers a Dialer with given name. func RegisterTransportDialer(protocol string, dialer dialFunc) error { if _, found := transportDialerCache[protocol]; found { - return errors.New(protocol, " dialer already registered").AtError() + return newError(protocol, " dialer already registered").AtError() } transportDialerCache[protocol] = dialer return nil @@ -51,7 +44,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea if streamSettings == nil { s, err := ToMemoryStreamConfig(nil) if err != nil { - return nil, errors.New("failed to create default stream settings").Base(err) + return nil, newError("failed to create default stream settings").Base(err) } streamSettings = s } @@ -59,7 +52,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea protocol := streamSettings.ProtocolName dialer := transportDialerCache[protocol] if dialer == nil { - return nil, errors.New(protocol, " dialer not registered").AtError() + return nil, newError(protocol, " dialer not registered").AtError() } return dialer(ctx, dest, streamSettings) } @@ -67,17 +60,12 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea if dest.Network == net.Network_UDP { udpDialer := transportDialerCache["udp"] if udpDialer == nil { - return nil, errors.New("UDP dialer not registered").AtError() + return nil, newError("UDP dialer not registered").AtError() } return udpDialer(ctx, dest, streamSettings) } - return nil, errors.New("unknown network ", dest.Network) -} - -// DestIpAddress returns the ip of proxy server. It is useful in case of Android client, which prepare an IP before proxy connection is established -func DestIpAddress() net.IP { - return effectiveSystemDialer.DestIpAddress() + return nil, newError("unknown network ", dest.Network) } var ( @@ -87,191 +75,86 @@ var ( func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) { if dnsClient == nil { - return nil, errors.New("DNS client not initialized").AtError() + return nil, nil } - ips, _, err := dnsClient.LookupIP(domain, dns.IPOption{ - IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && strategy.preferIP4(), - IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && strategy.preferIP6(), - }) - { // Resolve fallback - if (len(ips) == 0 || err != nil) && strategy.hasFallback() && localAddr == nil { - ips, _, err = dnsClient.LookupIP(domain, dns.IPOption{ - IPv4Enable: strategy.fallbackIP4(), - IPv6Enable: strategy.fallbackIP6(), - }) + option := dns.IPOption{ + IPv4Enable: true, + IPv6Enable: true, + FakeEnable: false, + } + + switch { + case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()): + option = dns.IPOption{ + IPv4Enable: true, + IPv6Enable: false, + FakeEnable: false, } + case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()): + option = dns.IPOption{ + IPv4Enable: false, + IPv6Enable: true, + FakeEnable: false, + } + case strategy == DomainStrategy_AS_IS: + return nil, nil } - if err == nil && len(ips) == 0 { - return nil, dns.ErrEmptyResponse - } - return ips, err + return dnsClient.LookupIP(domain, option) } -func canLookupIP(dst net.Destination, sockopt *SocketConfig) bool { - if dst.Address.Family().IsIP() { +func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool { + if dst.Address.Family().IsIP() || dnsClient == nil { return false } - return sockopt.DomainStrategy.hasStrategy() + return sockopt.DomainStrategy != DomainStrategy_AS_IS } -func redirect(ctx context.Context, dst net.Destination, obt string, h outbound.Handler) net.Conn { - errors.LogInfo(ctx, "redirecting request "+dst.String()+" to "+obt) - outbounds := session.OutboundsFromContext(ctx) - ctx = session.ContextWithOutbounds(ctx, append(outbounds, &session.Outbound{ - Target: dst, - Gateway: nil, - Tag: obt, - })) // add another outbound in session ctx +func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn { + newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx)) + h := obm.GetHandler(obt) + ctx = session.ContextWithOutbound(ctx, &session.Outbound{Target: dst, Gateway: nil}) + if h != nil { + ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...) + dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...) - ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...) - dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...) - - go h.Dispatch(context.WithoutCancel(ctx), &transport.Link{Reader: ur, Writer: dw}) - var readerOpt cnc.ConnectionOption - if dst.Network == net.Network_TCP { - readerOpt = cnc.ConnectionOutputMulti(dr) - } else { - readerOpt = cnc.ConnectionOutputMultiUDP(dr) + go h.Dispatch(ctx, &transport.Link{Reader: ur, Writer: dw}) + nc := cnc.NewConnection( + cnc.ConnectionInputMulti(uw), + cnc.ConnectionOutputMulti(dr), + cnc.ConnectionOnClose(common.ChainedClosable{uw, dw}), + ) + return nc } - nc := cnc.NewConnection( - cnc.ConnectionInputMulti(uw), - readerOpt, - cnc.ConnectionOnClose(common.ChainedClosable{uw, dw}), - ) - return nc - -} - -func checkAddressPortStrategy(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (*net.Destination, error) { - if sockopt.AddressPortStrategy == AddressPortStrategy_None { - return nil, nil - } - newDest := dest - var OverridePort, OverrideAddress bool - var OverrideBy string - switch sockopt.AddressPortStrategy { - case AddressPortStrategy_SrvPortOnly: - OverridePort = true - OverrideAddress = false - OverrideBy = "srv" - case AddressPortStrategy_SrvAddressOnly: - OverridePort = false - OverrideAddress = true - OverrideBy = "srv" - case AddressPortStrategy_SrvPortAndAddress: - OverridePort = true - OverrideAddress = true - OverrideBy = "srv" - case AddressPortStrategy_TxtPortOnly: - OverridePort = true - OverrideAddress = false - OverrideBy = "txt" - case AddressPortStrategy_TxtAddressOnly: - OverridePort = false - OverrideAddress = true - OverrideBy = "txt" - case AddressPortStrategy_TxtPortAndAddress: - OverridePort = true - OverrideAddress = true - OverrideBy = "txt" - default: - return nil, errors.New("unknown AddressPortStrategy") - } - - if !dest.Address.Family().IsDomain() { - return nil, nil - } - - if OverrideBy == "srv" { - errors.LogDebug(ctx, "query SRV record for "+dest.Address.String()) - parts := strings.SplitN(dest.Address.String(), ".", 3) - if len(parts) != 3 { - return nil, errors.New("invalid address format", dest.Address.String()) - } - _, srvRecords, err := gonet.DefaultResolver.LookupSRV(context.Background(), parts[0][1:], parts[1][1:], parts[2]) - if err != nil { - return nil, errors.New("failed to lookup SRV record").Base(err) - } - errors.LogDebug(ctx, "SRV record: "+fmt.Sprintf("addr=%s, port=%d, priority=%d, weight=%d", srvRecords[0].Target, srvRecords[0].Port, srvRecords[0].Priority, srvRecords[0].Weight)) - if OverridePort { - newDest.Port = net.Port(srvRecords[0].Port) - } - if OverrideAddress { - newDest.Address = net.ParseAddress(srvRecords[0].Target) - } - return &newDest, nil - } - if OverrideBy == "txt" { - errors.LogDebug(ctx, "query TXT record for "+dest.Address.String()) - txtRecords, err := gonet.DefaultResolver.LookupTXT(ctx, dest.Address.String()) - if err != nil { - errors.LogError(ctx, "failed to lookup SRV record: "+err.Error()) - return nil, errors.New("failed to lookup SRV record").Base(err) - } - for _, txtRecord := range txtRecords { - errors.LogDebug(ctx, "TXT record: "+txtRecord) - addr_s, port_s, _ := net.SplitHostPort(string(txtRecord)) - addr := net.ParseAddress(addr_s) - port, err := net.PortFromString(port_s) - if err != nil { - continue - } - - if OverridePort { - newDest.Port = port - } - if OverrideAddress { - newDest.Address = addr - } - return &newDest, nil - } - } - return nil, nil + return nil } // DialSystem calls system dialer to create a network connection. func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { var src net.Address - outbounds := session.OutboundsFromContext(ctx) - if len(outbounds) > 0 { - ob := outbounds[len(outbounds)-1] - src = ob.Gateway + if outbound := session.OutboundFromContext(ctx); outbound != nil { + src = outbound.Gateway } if sockopt == nil { return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) } - if newDest, err := checkAddressPortStrategy(ctx, dest, sockopt); err == nil && newDest != nil { - errors.LogInfo(ctx, "replace destination with "+newDest.String()) - dest = *newDest - } - - if canLookupIP(dest, sockopt) { + if canLookupIP(ctx, dest, sockopt) { ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src) - if err != nil { - errors.LogErrorInner(ctx, err, "failed to resolve ip") - if sockopt.DomainStrategy.forceIP() { - return nil, err - } - } else if sockopt.HappyEyeballs == nil || sockopt.HappyEyeballs.TryDelayMs == 0 || sockopt.HappyEyeballs.MaxConcurrentTry == 0 || len(ips) < 2 || len(sockopt.DialerProxy) > 0 || dest.Network != net.Network_TCP { + if err == nil && len(ips) > 0 { dest.Address = net.IPAddress(ips[dice.Roll(len(ips))]) - errors.LogInfo(ctx, "replace destination with "+dest.String()) - } else { - return TcpRaceDial(ctx, src, ips, dest.Port, sockopt) + newError("replace destination with " + dest.String()).AtInfo().WriteToLog() + } else if err != nil { + newError("failed to resolve ip").Base(err).AtWarning().WriteToLog() } } - if len(sockopt.DialerProxy) > 0 { - if obm == nil { - return nil, errors.New("there is no outbound manager for dialerProxy").AtError() + if obm != nil && len(sockopt.DialerProxy) > 0 { + nc := redirect(ctx, dest, sockopt.DialerProxy) + if nc != nil { + return nc, nil } - h := obm.GetHandler(sockopt.DialerProxy) - if h == nil { - return nil, errors.New("there is no outbound handler for dialerProxy").AtError() - } - return redirect(ctx, dest, sockopt.DialerProxy, h), nil } return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) diff --git a/transport/internet/domainsocket/config.go b/transport/internet/domainsocket/config.go new file mode 100644 index 00000000..7dfb5ec4 --- /dev/null +++ b/transport/internet/domainsocket/config.go @@ -0,0 +1,38 @@ +package domainsocket + +import ( + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/transport/internet" +) + +const ( + protocolName = "domainsocket" + sizeofSunPath = 108 +) + +func (c *Config) GetUnixAddr() (*net.UnixAddr, error) { + path := c.Path + if path == "" { + return nil, newError("empty domain socket path") + } + if c.Abstract && path[0] != '@' { + path = "@" + path + } + if c.Abstract && c.Padding { + raw := []byte(path) + addr := make([]byte, sizeofSunPath) + copy(addr, raw) + path = string(addr) + } + return &net.UnixAddr{ + Name: path, + Net: "unix", + }, nil +} + +func init() { + common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { + return new(Config) + })) +} diff --git a/transport/internet/domainsocket/config.pb.go b/transport/internet/domainsocket/config.pb.go new file mode 100644 index 00000000..23628e02 --- /dev/null +++ b/transport/internet/domainsocket/config.pb.go @@ -0,0 +1,180 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: transport/internet/domainsocket/config.proto + +package domainsocket + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Path of the domain socket. This overrides the IP/Port parameter from + // upstream caller. + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + // Abstract speicifies whether to use abstract namespace or not. + // Traditionally Unix domain socket is file system based. Abstract domain + // socket can be used without acquiring file lock. + Abstract bool `protobuf:"varint,2,opt,name=abstract,proto3" json:"abstract,omitempty"` + // Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to + // connect(2) or bind(2) when using abstract UDS. + Padding bool `protobuf:"varint,3,opt,name=padding,proto3" json:"padding,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_domainsocket_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_domainsocket_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Config.ProtoReflect.Descriptor instead. +func (*Config) Descriptor() ([]byte, []int) { + return file_transport_internet_domainsocket_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Config) GetAbstract() bool { + if x != nil { + return x.Abstract + } + return false +} + +func (x *Config) GetPadding() bool { + if x != nil { + return x.Padding + } + return false +} + +var File_transport_internet_domainsocket_config_proto protoreflect.FileDescriptor + +var file_transport_internet_domainsocket_config_proto_rawDesc = []byte{ + 0x0a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x24, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x22, 0x52, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x8e, 0x01, 0x0a, 0x28, 0x63, 0x6f, 0x6d, + 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, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x39, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0xaa, 0x02, 0x24, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_transport_internet_domainsocket_config_proto_rawDescOnce sync.Once + file_transport_internet_domainsocket_config_proto_rawDescData = file_transport_internet_domainsocket_config_proto_rawDesc +) + +func file_transport_internet_domainsocket_config_proto_rawDescGZIP() []byte { + file_transport_internet_domainsocket_config_proto_rawDescOnce.Do(func() { + file_transport_internet_domainsocket_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_domainsocket_config_proto_rawDescData) + }) + return file_transport_internet_domainsocket_config_proto_rawDescData +} + +var file_transport_internet_domainsocket_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_domainsocket_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.transport.internet.domainsocket.Config +} +var file_transport_internet_domainsocket_config_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_transport_internet_domainsocket_config_proto_init() } +func file_transport_internet_domainsocket_config_proto_init() { + if File_transport_internet_domainsocket_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_transport_internet_domainsocket_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_transport_internet_domainsocket_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_domainsocket_config_proto_goTypes, + DependencyIndexes: file_transport_internet_domainsocket_config_proto_depIdxs, + MessageInfos: file_transport_internet_domainsocket_config_proto_msgTypes, + }.Build() + File_transport_internet_domainsocket_config_proto = out.File + file_transport_internet_domainsocket_config_proto_rawDesc = nil + file_transport_internet_domainsocket_config_proto_goTypes = nil + file_transport_internet_domainsocket_config_proto_depIdxs = nil +} diff --git a/transport/internet/domainsocket/config.proto b/transport/internet/domainsocket/config.proto new file mode 100644 index 00000000..900dbbe3 --- /dev/null +++ b/transport/internet/domainsocket/config.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package xray.transport.internet.domainsocket; +option csharp_namespace = "Xray.Transport.Internet.DomainSocket"; +option go_package = "github.com/xtls/xray-core/transport/internet/domainsocket"; +option java_package = "com.xray.transport.internet.domainsocket"; +option java_multiple_files = true; + +message Config { + // Path of the domain socket. This overrides the IP/Port parameter from + // upstream caller. + string path = 1; + // Abstract speicifies whether to use abstract namespace or not. + // Traditionally Unix domain socket is file system based. Abstract domain + // socket can be used without acquiring file lock. + bool abstract = 2; + // Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to + // connect(2) or bind(2) when using abstract UDS. + bool padding = 3; +} diff --git a/transport/internet/domainsocket/dial.go b/transport/internet/domainsocket/dial.go new file mode 100644 index 00000000..a0032b36 --- /dev/null +++ b/transport/internet/domainsocket/dial.go @@ -0,0 +1,40 @@ +//go:build !windows && !wasm +// +build !windows,!wasm + +package domainsocket + +import ( + "context" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/reality" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" +) + +func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { + settings := streamSettings.ProtocolSettings.(*Config) + addr, err := settings.GetUnixAddr() + if err != nil { + return nil, err + } + + conn, err := net.DialUnix("unix", nil, addr) + if err != nil { + return nil, newError("failed to dial unix: ", settings.Path).Base(err).AtWarning() + } + + if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { + return tls.Client(conn, config.GetTLSConfig(tls.WithDestination(dest))), nil + } else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil { + return reality.UClient(conn, config, ctx, dest) + } + + return conn, nil +} + +func init() { + common.Must(internet.RegisterTransportDialer(protocolName, Dial)) +} diff --git a/transport/internet/domainsocket/errgen.go b/transport/internet/domainsocket/errgen.go new file mode 100644 index 00000000..83ac04d0 --- /dev/null +++ b/transport/internet/domainsocket/errgen.go @@ -0,0 +1,3 @@ +package domainsocket + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/domainsocket/errors.generated.go b/transport/internet/domainsocket/errors.generated.go new file mode 100644 index 00000000..451b1aa6 --- /dev/null +++ b/transport/internet/domainsocket/errors.generated.go @@ -0,0 +1,9 @@ +package domainsocket + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/domainsocket/listener.go b/transport/internet/domainsocket/listener.go new file mode 100644 index 00000000..323321e4 --- /dev/null +++ b/transport/internet/domainsocket/listener.go @@ -0,0 +1,140 @@ +//go:build !windows && !wasm +// +build !windows,!wasm + +package domainsocket + +import ( + "context" + gotls "crypto/tls" + "os" + "strings" + + goreality "github.com/xtls/reality" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/reality" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" + "golang.org/x/sys/unix" +) + +type Listener struct { + addr *net.UnixAddr + ln net.Listener + tlsConfig *gotls.Config + realityConfig *goreality.Config + config *Config + addConn internet.ConnHandler + locker *fileLocker +} + +func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { + settings := streamSettings.ProtocolSettings.(*Config) + addr, err := settings.GetUnixAddr() + if err != nil { + return nil, err + } + + unixListener, err := net.ListenUnix("unix", addr) + if err != nil { + return nil, newError("failed to listen domain socket").Base(err).AtWarning() + } + + ln := &Listener{ + addr: addr, + ln: unixListener, + config: settings, + addConn: handler, + } + + if !settings.Abstract { + ln.locker = &fileLocker{ + path: settings.Path + ".lock", + } + if err := ln.locker.Acquire(); err != nil { + unixListener.Close() + return nil, err + } + } + + if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { + ln.tlsConfig = config.GetTLSConfig() + } + if config := reality.ConfigFromStreamSettings(streamSettings); config != nil { + ln.realityConfig = config.GetREALITYConfig() + } + + go ln.run() + + return ln, nil +} + +func (ln *Listener) Addr() net.Addr { + return ln.addr +} + +func (ln *Listener) Close() error { + if ln.locker != nil { + ln.locker.Release() + } + return ln.ln.Close() +} + +func (ln *Listener) run() { + for { + conn, err := ln.ln.Accept() + if err != nil { + if strings.Contains(err.Error(), "closed") { + break + } + newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog() + continue + } + go func() { + if ln.tlsConfig != nil { + conn = tls.Server(conn, ln.tlsConfig) + } else if ln.realityConfig != nil { + if conn, err = reality.Server(conn, ln.realityConfig); err != nil { + newError(err).AtInfo().WriteToLog() + return + } + } + ln.addConn(stat.Connection(conn)) + }() + } +} + +type fileLocker struct { + path string + file *os.File +} + +func (fl *fileLocker) Acquire() error { + f, err := os.Create(fl.path) + if err != nil { + return err + } + if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { + f.Close() + return newError("failed to lock file: ", fl.path).Base(err) + } + fl.file = f + return nil +} + +func (fl *fileLocker) Release() { + if err := unix.Flock(int(fl.file.Fd()), unix.LOCK_UN); err != nil { + newError("failed to unlock file: ", fl.path).Base(err).WriteToLog() + } + if err := fl.file.Close(); err != nil { + newError("failed to close file: ", fl.path).Base(err).WriteToLog() + } + if err := os.Remove(fl.path); err != nil { + newError("failed to remove file: ", fl.path).Base(err).WriteToLog() + } +} + +func init() { + common.Must(internet.RegisterTransportListener(protocolName, Listen)) +} diff --git a/transport/internet/domainsocket/listener_test.go b/transport/internet/domainsocket/listener_test.go new file mode 100644 index 00000000..308fa07b --- /dev/null +++ b/transport/internet/domainsocket/listener_test.go @@ -0,0 +1,94 @@ +//go:build !windows && !android +// +build !windows,!android + +package domainsocket_test + +import ( + "context" + "runtime" + "testing" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/transport/internet" + . "github.com/xtls/xray-core/transport/internet/domainsocket" + "github.com/xtls/xray-core/transport/internet/stat" +) + +func TestListen(t *testing.T) { + ctx := context.Background() + streamSettings := &internet.MemoryStreamConfig{ + ProtocolName: "domainsocket", + ProtocolSettings: &Config{ + Path: "/tmp/ts3", + }, + } + listener, err := Listen(ctx, nil, net.Port(0), streamSettings, func(conn stat.Connection) { + defer conn.Close() + + b := buf.New() + defer b.Release() + common.Must2(b.ReadFrom(conn)) + b.WriteString("Response") + + common.Must2(conn.Write(b.Bytes())) + }) + common.Must(err) + defer listener.Close() + + conn, err := Dial(ctx, net.Destination{}, streamSettings) + common.Must(err) + defer conn.Close() + + common.Must2(conn.Write([]byte("Request"))) + + b := buf.New() + defer b.Release() + common.Must2(b.ReadFrom(conn)) + + if b.String() != "RequestResponse" { + t.Error("expected response as 'RequestResponse' but got ", b.String()) + } +} + +func TestListenAbstract(t *testing.T) { + if runtime.GOOS != "linux" { + return + } + + ctx := context.Background() + streamSettings := &internet.MemoryStreamConfig{ + ProtocolName: "domainsocket", + ProtocolSettings: &Config{ + Path: "/tmp/ts3", + Abstract: true, + }, + } + listener, err := Listen(ctx, nil, net.Port(0), streamSettings, func(conn stat.Connection) { + defer conn.Close() + + b := buf.New() + defer b.Release() + common.Must2(b.ReadFrom(conn)) + b.WriteString("Response") + + common.Must2(conn.Write(b.Bytes())) + }) + common.Must(err) + defer listener.Close() + + conn, err := Dial(ctx, net.Destination{}, streamSettings) + common.Must(err) + defer conn.Close() + + common.Must2(conn.Write([]byte("Request"))) + + b := buf.New() + defer b.Release() + common.Must2(b.ReadFrom(conn)) + + if b.String() != "RequestResponse" { + t.Error("expected response as 'RequestResponse' but got ", b.String()) + } +} diff --git a/transport/internet/errors.generated.go b/transport/internet/errors.generated.go new file mode 100644 index 00000000..caf158a1 --- /dev/null +++ b/transport/internet/errors.generated.go @@ -0,0 +1,9 @@ +package internet + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/filelocker_other.go b/transport/internet/filelocker_other.go index 36d937c1..28fc0f58 100644 --- a/transport/internet/filelocker_other.go +++ b/transport/internet/filelocker_other.go @@ -4,10 +4,8 @@ package internet import ( - "context" "os" - "github.com/xtls/xray-core/common/errors" "golang.org/x/sys/unix" ) @@ -19,7 +17,7 @@ func (fl *FileLocker) Acquire() error { } if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { f.Close() - return errors.New("failed to lock file: ", fl.path).Base(err) + return newError("failed to lock file: ", fl.path).Base(err) } fl.file = f return nil @@ -28,12 +26,12 @@ func (fl *FileLocker) Acquire() error { // Release lock func (fl *FileLocker) Release() { if err := unix.Flock(int(fl.file.Fd()), unix.LOCK_UN); err != nil { - errors.LogInfoInner(context.Background(), err, "failed to unlock file: ", fl.path) + newError("failed to unlock file: ", fl.path).Base(err).WriteToLog() } if err := fl.file.Close(); err != nil { - errors.LogInfoInner(context.Background(), err, "failed to close file: ", fl.path) + newError("failed to close file: ", fl.path).Base(err).WriteToLog() } if err := os.Remove(fl.path); err != nil { - errors.LogInfoInner(context.Background(), err, "failed to remove file: ", fl.path) + newError("failed to remove file: ", fl.path).Base(err).WriteToLog() } } diff --git a/transport/internet/grpc/config.go b/transport/internet/grpc/config.go index c9903018..d87722a4 100644 --- a/transport/internet/grpc/config.go +++ b/transport/internet/grpc/config.go @@ -2,58 +2,19 @@ package grpc import ( "net/url" - "strings" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/transport/internet" ) +const protocolName = "grpc" + func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } -func (c *Config) getServiceName() string { - // Normal old school config - if !strings.HasPrefix(c.ServiceName, "/") { - return url.PathEscape(c.ServiceName) - } - - // Otherwise new custom paths - lastIndex := strings.LastIndex(c.ServiceName, "/") - if lastIndex < 1 { - lastIndex = 1 - } - rawServiceName := c.ServiceName[1:lastIndex] // trim from first to last '/' - serviceNameParts := strings.Split(rawServiceName, "/") - for i := range serviceNameParts { - serviceNameParts[i] = url.PathEscape(serviceNameParts[i]) - } - return strings.Join(serviceNameParts, "/") -} - -func (c *Config) getTunStreamName() string { - // Normal old school config - if !strings.HasPrefix(c.ServiceName, "/") { - return "Tun" - } - // Otherwise new custom paths - endingPath := c.ServiceName[strings.LastIndex(c.ServiceName, "/")+1:] // from the last '/' to end of string - return url.PathEscape(strings.Split(endingPath, "|")[0]) -} - -func (c *Config) getTunMultiStreamName() string { - // Normal old school config - if !strings.HasPrefix(c.ServiceName, "/") { - return "TunMulti" - } - // Otherwise new custom paths - endingPath := c.ServiceName[strings.LastIndex(c.ServiceName, "/")+1:] // from the last '/' to end of string - streamNames := strings.Split(endingPath, "|") - if len(streamNames) == 1 { // client side. Service name is the full path to multi tun - return url.PathEscape(streamNames[0]) - } else { // server side. The second part is the path to multi tun - return url.PathEscape(streamNames[1]) - } +func (c *Config) getNormalizedName() string { + return url.PathEscape(c.ServiceName) } diff --git a/transport/internet/grpc/config.pb.go b/transport/internet/grpc/config.pb.go index f6aecf47..9a94984d 100644 --- a/transport/internet/grpc/config.pb.go +++ b/transport/internet/grpc/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/grpc/config.proto package grpc @@ -25,21 +25,22 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` MultiMode bool `protobuf:"varint,3,opt,name=multi_mode,json=multiMode,proto3" json:"multi_mode,omitempty"` IdleTimeout int32 `protobuf:"varint,4,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` HealthCheckTimeout int32 `protobuf:"varint,5,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` PermitWithoutStream bool `protobuf:"varint,6,opt,name=permit_without_stream,json=permitWithoutStream,proto3" json:"permit_without_stream,omitempty"` InitialWindowsSize int32 `protobuf:"varint,7,opt,name=initial_windows_size,json=initialWindowsSize,proto3" json:"initial_windows_size,omitempty"` - UserAgent string `protobuf:"bytes,8,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_grpc_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_grpc_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -50,7 +51,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_grpc_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -65,9 +66,9 @@ func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_grpc_config_proto_rawDescGZIP(), []int{0} } -func (x *Config) GetAuthority() string { +func (x *Config) GetHost() string { if x != nil { - return x.Authority + return x.Host } return "" } @@ -114,13 +115,6 @@ func (x *Config) GetInitialWindowsSize() int32 { return 0 } -func (x *Config) GetUserAgent() string { - if x != nil { - return x.UserAgent - } - return "" -} - var File_transport_internet_grpc_config_proto protoreflect.FileDescriptor var file_transport_internet_grpc_config_proto_rawDesc = []byte{ @@ -128,31 +122,29 @@ var file_transport_internet_grpc_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x25, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, - 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0xc2, 0x02, - 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x75, 0x6c, - 0x74, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, - 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, - 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, - 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x32, 0x0a, - 0x15, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, - 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x74, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x77, 0x69, 0x6e, - 0x64, 0x6f, 0x77, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x53, - 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x42, 0x33, 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, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x99, 0x02, + 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, + 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x5f, 0x77, 0x69, + 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, + 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x33, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -168,7 +160,7 @@ func file_transport_internet_grpc_config_proto_rawDescGZIP() []byte { } var file_transport_internet_grpc_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_grpc_config_proto_goTypes = []any{ +var file_transport_internet_grpc_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.transport.internet.grpc.encoding.Config } var file_transport_internet_grpc_config_proto_depIdxs = []int32{ @@ -184,6 +176,20 @@ func file_transport_internet_grpc_config_proto_init() { if File_transport_internet_grpc_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_grpc_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/grpc/config.proto b/transport/internet/grpc/config.proto index fcaa2ed9..7d256b2c 100644 --- a/transport/internet/grpc/config.proto +++ b/transport/internet/grpc/config.proto @@ -4,12 +4,11 @@ package xray.transport.internet.grpc.encoding; option go_package = "github.com/xtls/xray-core/transport/internet/grpc"; message Config { - string authority = 1; + string host = 1; string service_name = 2; bool multi_mode = 3; int32 idle_timeout = 4; int32 health_check_timeout = 5; bool permit_without_stream = 6; int32 initial_windows_size = 7; - string user_agent = 8; } diff --git a/transport/internet/grpc/config_test.go b/transport/internet/grpc/config_test.go deleted file mode 100644 index b159ffdf..00000000 --- a/transport/internet/grpc/config_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package grpc - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestConfig_GetServiceName(t *testing.T) { - tests := []struct { - TestName string - ServiceName string - Expected string - }{ - { - TestName: "simple no absolute path", - ServiceName: "hello", - Expected: "hello", - }, - { - TestName: "escape no absolute path", - ServiceName: "hello/world!", - Expected: "hello%2Fworld%21", - }, - { - TestName: "absolute path", - ServiceName: "/my/sample/path/a|b", - Expected: "my/sample/path", - }, - { - TestName: "escape absolute path", - ServiceName: "/hello /world!/a|b", - Expected: "hello%20/world%21", - }, - { - TestName: "path with only one '/'", - ServiceName: "/foo", - Expected: "", - }, - } - for _, test := range tests { - t.Run(test.TestName, func(t *testing.T) { - config := Config{ServiceName: test.ServiceName} - assert.Equal(t, test.Expected, config.getServiceName()) - }) - } -} - -func TestConfig_GetTunStreamName(t *testing.T) { - tests := []struct { - TestName string - ServiceName string - Expected string - }{ - { - TestName: "no absolute path", - ServiceName: "hello", - Expected: "Tun", - }, - { - TestName: "absolute path server", - ServiceName: "/my/sample/path/tun_service|multi_service", - Expected: "tun_service", - }, - { - TestName: "absolute path client", - ServiceName: "/my/sample/path/tun_service", - Expected: "tun_service", - }, - { - TestName: "escape absolute path client", - ServiceName: "/m y/sa !mple/pa\\th/tun\\_serv!ice", - Expected: "tun%5C_serv%21ice", - }, - } - for _, test := range tests { - t.Run(test.TestName, func(t *testing.T) { - config := Config{ServiceName: test.ServiceName} - assert.Equal(t, test.Expected, config.getTunStreamName()) - }) - } -} - -func TestConfig_GetTunMultiStreamName(t *testing.T) { - tests := []struct { - TestName string - ServiceName string - Expected string - }{ - { - TestName: "no absolute path", - ServiceName: "hello", - Expected: "TunMulti", - }, - { - TestName: "absolute path server", - ServiceName: "/my/sample/path/tun_service|multi_service", - Expected: "multi_service", - }, - { - TestName: "absolute path client", - ServiceName: "/my/sample/path/multi_service", - Expected: "multi_service", - }, - { - TestName: "escape absolute path client", - ServiceName: "/m y/sa !mple/pa\\th/mu%lti\\_serv!ice", - Expected: "mu%25lti%5C_serv%21ice", - }, - } - for _, test := range tests { - t.Run(test.TestName, func(t *testing.T) { - config := Config{ServiceName: test.ServiceName} - assert.Equal(t, test.Expected, config.getTunMultiStreamName()) - }) - } -} diff --git a/transport/internet/grpc/dial.go b/transport/internet/grpc/dial.go index b8740dae..4ab4b615 100644 --- a/transport/internet/grpc/dial.go +++ b/transport/internet/grpc/dial.go @@ -7,8 +7,6 @@ import ( "time" "github.com/xtls/xray-core/common" - c "github.com/xtls/xray-core/common/ctx" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" @@ -19,16 +17,16 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" ) func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { - errors.LogInfo(ctx, "creating connection to ", dest) + newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := dialgRPC(ctx, dest, streamSettings) if err != nil { - return nil, errors.New("failed to dial gRPC").Base(err) + return nil, newError("failed to dial gRPC").Base(err) } return stat.Connection(conn), nil } @@ -52,22 +50,21 @@ func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *interne conn, err := getGrpcClient(ctx, dest, streamSettings) if err != nil { - return nil, errors.New("Cannot dial gRPC").Base(err) + return nil, newError("Cannot dial gRPC").Base(err) } client := encoding.NewGRPCServiceClient(conn) if grpcSettings.MultiMode { - errors.LogDebug(ctx, "using gRPC multi mode service name: `"+grpcSettings.getServiceName()+"` stream name: `"+grpcSettings.getTunMultiStreamName()+"`") - grpcService, err := client.(encoding.GRPCServiceClientX).TunMultiCustomName(ctx, grpcSettings.getServiceName(), grpcSettings.getTunMultiStreamName()) + newError("using gRPC multi mode").AtDebug().WriteToLog() + grpcService, err := client.(encoding.GRPCServiceClientX).TunMultiCustomName(ctx, grpcSettings.getNormalizedName()) if err != nil { - return nil, errors.New("Cannot dial gRPC").Base(err) + return nil, newError("Cannot dial gRPC").Base(err) } return encoding.NewMultiHunkConn(grpcService, nil), nil } - errors.LogDebug(ctx, "using gRPC tun mode service name: `"+grpcSettings.getServiceName()+"` stream name: `"+grpcSettings.getTunStreamName()+"`") - grpcService, err := client.(encoding.GRPCServiceClientX).TunCustomName(ctx, grpcSettings.getServiceName(), grpcSettings.getTunStreamName()) + grpcService, err := client.(encoding.GRPCServiceClientX).TunCustomName(ctx, grpcSettings.getNormalizedName()) if err != nil { - return nil, errors.New("Cannot dial gRPC").Base(err) + return nil, newError("Cannot dial gRPC").Base(err) } return encoding.NewHunkConn(grpcService, nil), nil @@ -100,13 +97,16 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in MinConnectTimeout: 5 * time.Second, }), grpc.WithContextDialer(func(gctx context.Context, s string) (gonet.Conn, error) { + gctx = session.ContextWithID(gctx, session.IDFromContext(ctx)) + gctx = session.ContextWithOutbound(gctx, session.OutboundFromContext(ctx)) + + rawHost, rawPort, err := net.SplitHostPort(s) select { case <-gctx.Done(): return nil, gctx.Err() default: } - rawHost, rawPort, err := net.SplitHostPort(s) if err != nil { return nil, err } @@ -118,43 +118,25 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in return nil, err } address := net.ParseAddress(rawHost) - - gctx = c.ContextWithID(gctx, c.IDFromContext(ctx)) - gctx = session.ContextWithOutbounds(gctx, session.OutboundsFromContext(ctx)) - gctx = session.ContextWithTimeoutOnly(gctx, true) - c, err := internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt) - if err == nil { - if tlsConfig != nil { - config := tlsConfig.GetTLSConfig() - if config.ServerName == "" && address.Family().IsDomain() { - config.ServerName = address.Domain() - } - if fingerprint := tls.GetFingerprint(tlsConfig.Fingerprint); fingerprint != nil { - return tls.UClient(c, config, fingerprint), nil - } else { // Fallback to normal gRPC TLS - return tls.Client(c, config), nil - } - } - if realityConfig != nil { - return reality.UClient(c, realityConfig, gctx, dest) - } + if err == nil && realityConfig != nil { + return reality.UClient(c, realityConfig, ctx, dest) } return c, err }), } - dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials())) - - authority := "" - if grpcSettings.Authority != "" { - authority = grpcSettings.Authority - } else if tlsConfig != nil && tlsConfig.ServerName != "" { - authority = tlsConfig.ServerName - } else if realityConfig == nil && dest.Address.Family().IsDomain() { - authority = dest.Address.Domain() + if tlsConfig != nil { + var transportCredential credentials.TransportCredentials + if fingerprint := tls.GetFingerprint(tlsConfig.Fingerprint); fingerprint != nil { + transportCredential = tls.NewGrpcUtls(tlsConfig.GetTLSConfig(), fingerprint) + } else { // Fallback to normal gRPC TLS + transportCredential = credentials.NewTLS(tlsConfig.GetTLSConfig()) + } + dialOptions = append(dialOptions, grpc.WithTransportCredentials(transportCredential)) + } else { + dialOptions = append(dialOptions, grpc.WithInsecure()) } - dialOptions = append(dialOptions, grpc.WithAuthority(authority)) if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 || grpcSettings.PermitWithoutStream { dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{ @@ -168,10 +150,6 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in dialOptions = append(dialOptions, grpc.WithInitialWindowSize(grpcSettings.InitialWindowsSize)) } - if grpcSettings.UserAgent != "" { - dialOptions = append(dialOptions, grpc.WithUserAgent(grpcSettings.UserAgent)) - } - var grpcDestHost string if dest.Address.Family().IsDomain() { grpcDestHost = dest.Address.Domain() diff --git a/transport/internet/grpc/encoding/customSeviceName.go b/transport/internet/grpc/encoding/customSeviceName.go index 3b136fd3..aa098835 100644 --- a/transport/internet/grpc/encoding/customSeviceName.go +++ b/transport/internet/grpc/encoding/customSeviceName.go @@ -6,20 +6,20 @@ import ( "google.golang.org/grpc" ) -func ServerDesc(name, tun, tunMulti string) grpc.ServiceDesc { +func ServerDesc(name string) grpc.ServiceDesc { return grpc.ServiceDesc{ ServiceName: name, HandlerType: (*GRPCServiceServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { - StreamName: tun, + StreamName: "Tun", Handler: _GRPCService_Tun_Handler, ServerStreams: true, ClientStreams: true, }, { - StreamName: tunMulti, + StreamName: "TunMulti", Handler: _GRPCService_TunMulti_Handler, ServerStreams: true, ClientStreams: true, @@ -29,32 +29,32 @@ func ServerDesc(name, tun, tunMulti string) grpc.ServiceDesc { } } -func (c *gRPCServiceClient) TunCustomName(ctx context.Context, name, tun string, opts ...grpc.CallOption) (GRPCService_TunClient, error) { - stream, err := c.cc.NewStream(ctx, &ServerDesc(name, tun, "").Streams[0], "/"+name+"/"+tun, opts...) +func (c *gRPCServiceClient) TunCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GRPCService_TunClient, error) { + stream, err := c.cc.NewStream(ctx, &ServerDesc(name).Streams[0], "/"+name+"/Tun", opts...) if err != nil { return nil, err } - x := &grpc.GenericClientStream[Hunk, Hunk]{ClientStream: stream} + x := &gRPCServiceTunClient{stream} return x, nil } -func (c *gRPCServiceClient) TunMultiCustomName(ctx context.Context, name, tunMulti string, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) { - stream, err := c.cc.NewStream(ctx, &ServerDesc(name, "", tunMulti).Streams[1], "/"+name+"/"+tunMulti, opts...) +func (c *gRPCServiceClient) TunMultiCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) { + stream, err := c.cc.NewStream(ctx, &ServerDesc(name).Streams[1], "/"+name+"/TunMulti", opts...) if err != nil { return nil, err } - x := &grpc.GenericClientStream[MultiHunk, MultiHunk]{ClientStream: stream} + x := &gRPCServiceTunMultiClient{stream} return x, nil } type GRPCServiceClientX interface { - TunCustomName(ctx context.Context, name, tun string, opts ...grpc.CallOption) (GRPCService_TunClient, error) - TunMultiCustomName(ctx context.Context, name, tunMulti string, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) + TunCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GRPCService_TunClient, error) + TunMultiCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) Tun(ctx context.Context, opts ...grpc.CallOption) (GRPCService_TunClient, error) TunMulti(ctx context.Context, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) } -func RegisterGRPCServiceServerX(s *grpc.Server, srv GRPCServiceServer, name, tun, tunMulti string) { - desc := ServerDesc(name, tun, tunMulti) +func RegisterGRPCServiceServerX(s *grpc.Server, srv GRPCServiceServer, name string) { + desc := ServerDesc(name) s.RegisterService(&desc, srv) } diff --git a/transport/internet/grpc/encoding/encoding.go b/transport/internet/grpc/encoding/encoding.go index 523b90ce..f8e3e368 100644 --- a/transport/internet/grpc/encoding/encoding.go +++ b/transport/internet/grpc/encoding/encoding.go @@ -1 +1,3 @@ package encoding + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/grpc/encoding/errors.generated.go b/transport/internet/grpc/encoding/errors.generated.go new file mode 100644 index 00000000..267711d9 --- /dev/null +++ b/transport/internet/grpc/encoding/errors.generated.go @@ -0,0 +1,9 @@ +package encoding + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/grpc/encoding/hunkconn.go b/transport/internet/grpc/encoding/hunkconn.go index f295f33a..cfa0d527 100644 --- a/transport/internet/grpc/encoding/hunkconn.go +++ b/transport/internet/grpc/encoding/hunkconn.go @@ -6,7 +6,6 @@ import ( "net" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" xnet "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/signal/done" @@ -80,7 +79,7 @@ func (h *HunkReaderWriter) forceFetch() error { return err } - return errors.New("failed to fetch hunk from gRPC tunnel").Base(err) + return newError("failed to fetch hunk from gRPC tunnel").Base(err) } h.buf = hunk.Data @@ -136,7 +135,7 @@ func (h *HunkReaderWriter) Write(buf []byte) (int, error) { err := h.hc.Send(&Hunk{Data: buf[:]}) if err != nil { - return 0, errors.New("failed to send data over gRPC tunnel").Base(err) + return 0, newError("failed to send data over gRPC tunnel").Base(err) } return len(buf), nil } diff --git a/transport/internet/grpc/encoding/multiconn.go b/transport/internet/grpc/encoding/multiconn.go index fa95cba8..ec503947 100644 --- a/transport/internet/grpc/encoding/multiconn.go +++ b/transport/internet/grpc/encoding/multiconn.go @@ -6,7 +6,6 @@ import ( "net" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" xnet "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/signal/done" @@ -75,7 +74,7 @@ func (h *MultiHunkReaderWriter) forceFetch() error { return err } - return errors.New("failed to fetch hunk from gRPC tunnel").Base(err) + return newError("failed to fetch hunk from gRPC tunnel").Base(err) } h.buf = hunk.Data diff --git a/transport/internet/grpc/encoding/stream.pb.go b/transport/internet/grpc/encoding/stream.pb.go index ad069a1c..c605613d 100644 --- a/transport/internet/grpc/encoding/stream.pb.go +++ b/transport/internet/grpc/encoding/stream.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/grpc/encoding/stream.proto package encoding @@ -30,9 +30,11 @@ type Hunk struct { func (x *Hunk) Reset() { *x = Hunk{} - mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Hunk) String() string { @@ -43,7 +45,7 @@ func (*Hunk) ProtoMessage() {} func (x *Hunk) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -75,9 +77,11 @@ type MultiHunk struct { func (x *MultiHunk) Reset() { *x = MultiHunk{} - mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *MultiHunk) String() string { @@ -88,7 +92,7 @@ func (*MultiHunk) ProtoMessage() {} func (x *MultiHunk) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -157,7 +161,7 @@ func file_transport_internet_grpc_encoding_stream_proto_rawDescGZIP() []byte { } var file_transport_internet_grpc_encoding_stream_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_transport_internet_grpc_encoding_stream_proto_goTypes = []any{ +var file_transport_internet_grpc_encoding_stream_proto_goTypes = []interface{}{ (*Hunk)(nil), // 0: xray.transport.internet.grpc.encoding.Hunk (*MultiHunk)(nil), // 1: xray.transport.internet.grpc.encoding.MultiHunk } @@ -178,6 +182,32 @@ func file_transport_internet_grpc_encoding_stream_proto_init() { if File_transport_internet_grpc_encoding_stream_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_grpc_encoding_stream_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Hunk); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_grpc_encoding_stream_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MultiHunk); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/grpc/encoding/stream_grpc.pb.go b/transport/internet/grpc/encoding/stream_grpc.pb.go index 1fe11524..ab75a5d8 100644 --- a/transport/internet/grpc/encoding/stream_grpc.pb.go +++ b/transport/internet/grpc/encoding/stream_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.2 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.12 // source: transport/internet/grpc/encoding/stream.proto package encoding @@ -15,20 +15,15 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - GRPCService_Tun_FullMethodName = "/xray.transport.internet.grpc.encoding.GRPCService/Tun" - GRPCService_TunMulti_FullMethodName = "/xray.transport.internet.grpc.encoding.GRPCService/TunMulti" -) +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 // GRPCServiceClient is the client API for GRPCService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type GRPCServiceClient interface { - Tun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Hunk, Hunk], error) - TunMulti(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[MultiHunk, MultiHunk], error) + Tun(ctx context.Context, opts ...grpc.CallOption) (GRPCService_TunClient, error) + TunMulti(ctx context.Context, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) } type gRPCServiceClient struct { @@ -39,56 +34,88 @@ func NewGRPCServiceClient(cc grpc.ClientConnInterface) GRPCServiceClient { return &gRPCServiceClient{cc} } -func (c *gRPCServiceClient) Tun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Hunk, Hunk], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &GRPCService_ServiceDesc.Streams[0], GRPCService_Tun_FullMethodName, cOpts...) +func (c *gRPCServiceClient) Tun(ctx context.Context, opts ...grpc.CallOption) (GRPCService_TunClient, error) { + stream, err := c.cc.NewStream(ctx, &GRPCService_ServiceDesc.Streams[0], "/xray.transport.internet.grpc.encoding.GRPCService/Tun", opts...) if err != nil { return nil, err } - x := &grpc.GenericClientStream[Hunk, Hunk]{ClientStream: stream} + x := &gRPCServiceTunClient{stream} return x, nil } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GRPCService_TunClient = grpc.BidiStreamingClient[Hunk, Hunk] +type GRPCService_TunClient interface { + Send(*Hunk) error + Recv() (*Hunk, error) + grpc.ClientStream +} -func (c *gRPCServiceClient) TunMulti(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[MultiHunk, MultiHunk], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &GRPCService_ServiceDesc.Streams[1], GRPCService_TunMulti_FullMethodName, cOpts...) +type gRPCServiceTunClient struct { + grpc.ClientStream +} + +func (x *gRPCServiceTunClient) Send(m *Hunk) error { + return x.ClientStream.SendMsg(m) +} + +func (x *gRPCServiceTunClient) Recv() (*Hunk, error) { + m := new(Hunk) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gRPCServiceClient) TunMulti(ctx context.Context, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) { + stream, err := c.cc.NewStream(ctx, &GRPCService_ServiceDesc.Streams[1], "/xray.transport.internet.grpc.encoding.GRPCService/TunMulti", opts...) if err != nil { return nil, err } - x := &grpc.GenericClientStream[MultiHunk, MultiHunk]{ClientStream: stream} + x := &gRPCServiceTunMultiClient{stream} return x, nil } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GRPCService_TunMultiClient = grpc.BidiStreamingClient[MultiHunk, MultiHunk] +type GRPCService_TunMultiClient interface { + Send(*MultiHunk) error + Recv() (*MultiHunk, error) + grpc.ClientStream +} + +type gRPCServiceTunMultiClient struct { + grpc.ClientStream +} + +func (x *gRPCServiceTunMultiClient) Send(m *MultiHunk) error { + return x.ClientStream.SendMsg(m) +} + +func (x *gRPCServiceTunMultiClient) Recv() (*MultiHunk, error) { + m := new(MultiHunk) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} // GRPCServiceServer is the server API for GRPCService service. // All implementations must embed UnimplementedGRPCServiceServer -// for forward compatibility. +// for forward compatibility type GRPCServiceServer interface { - Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error - TunMulti(grpc.BidiStreamingServer[MultiHunk, MultiHunk]) error + Tun(GRPCService_TunServer) error + TunMulti(GRPCService_TunMultiServer) error mustEmbedUnimplementedGRPCServiceServer() } -// UnimplementedGRPCServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedGRPCServiceServer struct{} +// UnimplementedGRPCServiceServer must be embedded to have forward compatible implementations. +type UnimplementedGRPCServiceServer struct { +} -func (UnimplementedGRPCServiceServer) Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error { +func (UnimplementedGRPCServiceServer) Tun(GRPCService_TunServer) error { return status.Errorf(codes.Unimplemented, "method Tun not implemented") } -func (UnimplementedGRPCServiceServer) TunMulti(grpc.BidiStreamingServer[MultiHunk, MultiHunk]) error { +func (UnimplementedGRPCServiceServer) TunMulti(GRPCService_TunMultiServer) error { return status.Errorf(codes.Unimplemented, "method TunMulti not implemented") } func (UnimplementedGRPCServiceServer) mustEmbedUnimplementedGRPCServiceServer() {} -func (UnimplementedGRPCServiceServer) testEmbeddedByValue() {} // UnsafeGRPCServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GRPCServiceServer will @@ -98,29 +125,60 @@ type UnsafeGRPCServiceServer interface { } func RegisterGRPCServiceServer(s grpc.ServiceRegistrar, srv GRPCServiceServer) { - // If the following call pancis, it indicates UnimplementedGRPCServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&GRPCService_ServiceDesc, srv) } func _GRPCService_Tun_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(GRPCServiceServer).Tun(&grpc.GenericServerStream[Hunk, Hunk]{ServerStream: stream}) + return srv.(GRPCServiceServer).Tun(&gRPCServiceTunServer{stream}) } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GRPCService_TunServer = grpc.BidiStreamingServer[Hunk, Hunk] +type GRPCService_TunServer interface { + Send(*Hunk) error + Recv() (*Hunk, error) + grpc.ServerStream +} + +type gRPCServiceTunServer struct { + grpc.ServerStream +} + +func (x *gRPCServiceTunServer) Send(m *Hunk) error { + return x.ServerStream.SendMsg(m) +} + +func (x *gRPCServiceTunServer) Recv() (*Hunk, error) { + m := new(Hunk) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} func _GRPCService_TunMulti_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(GRPCServiceServer).TunMulti(&grpc.GenericServerStream[MultiHunk, MultiHunk]{ServerStream: stream}) + return srv.(GRPCServiceServer).TunMulti(&gRPCServiceTunMultiServer{stream}) } -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GRPCService_TunMultiServer = grpc.BidiStreamingServer[MultiHunk, MultiHunk] +type GRPCService_TunMultiServer interface { + Send(*MultiHunk) error + Recv() (*MultiHunk, error) + grpc.ServerStream +} + +type gRPCServiceTunMultiServer struct { + grpc.ServerStream +} + +func (x *gRPCServiceTunMultiServer) Send(m *MultiHunk) error { + return x.ServerStream.SendMsg(m) +} + +func (x *gRPCServiceTunMultiServer) Recv() (*MultiHunk, error) { + m := new(MultiHunk) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} // GRPCService_ServiceDesc is the grpc.ServiceDesc for GRPCService service. // It's only intended for direct use with grpc.RegisterService, diff --git a/transport/internet/grpc/errors.generated.go b/transport/internet/grpc/errors.generated.go new file mode 100644 index 00000000..290cc82f --- /dev/null +++ b/transport/internet/grpc/errors.generated.go @@ -0,0 +1,9 @@ +package grpc + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/grpc/grpc.go b/transport/internet/grpc/grpc.go index bcf78b0a..6caf155b 100644 --- a/transport/internet/grpc/grpc.go +++ b/transport/internet/grpc/grpc.go @@ -1,3 +1,3 @@ package grpc -const protocolName = "grpc" +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/grpc/hub.go b/transport/internet/grpc/hub.go index ae8788fa..9bce2274 100644 --- a/transport/internet/grpc/hub.go +++ b/transport/internet/grpc/hub.go @@ -6,8 +6,8 @@ import ( goreality "github.com/xtls/reality" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/grpc/encoding" "github.com/xtls/xray-core/transport/internet/reality" @@ -23,6 +23,7 @@ type Listener struct { handler internet.ConnHandler local net.Addr config *Config + locker *internet.FileLocker // for unix domain socket s *grpc.Server } @@ -94,7 +95,7 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i listener.s = s if settings.SocketSettings != nil && settings.SocketSettings.AcceptProxyProtocol { - errors.LogWarning(ctx, "accepting PROXY protocol") + newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } go func() { @@ -106,28 +107,31 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i Net: "unix", }, settings.SocketSettings) if err != nil { - errors.LogErrorInner(ctx, err, "failed to listen on ", address) + newError("failed to listen on ", address).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) return } + locker := ctx.Value(address.Domain()) + if locker != nil { + listener.locker = locker.(*internet.FileLocker) + } } else { // tcp streamListener, err = internet.ListenSystem(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, settings.SocketSettings) if err != nil { - errors.LogErrorInner(ctx, err, "failed to listen on ", address, ":", port) + newError("failed to listen on ", address, ":", port).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) return } } - errors.LogDebug(ctx, "gRPC listen for service name `"+grpcSettings.getServiceName()+"` tun `"+grpcSettings.getTunStreamName()+"` multi tun `"+grpcSettings.getTunMultiStreamName()+"`") - encoding.RegisterGRPCServiceServerX(s, listener, grpcSettings.getServiceName(), grpcSettings.getTunStreamName(), grpcSettings.getTunMultiStreamName()) + encoding.RegisterGRPCServiceServerX(s, listener, grpcSettings.getNormalizedName()) if config := reality.ConfigFromStreamSettings(settings); config != nil { streamListener = goreality.NewListener(streamListener, config.GetREALITYConfig()) } if err = s.Serve(streamListener); err != nil { - errors.LogInfoInner(ctx, err, "Listener for gRPC ended") + newError("Listener for gRPC ended").Base(err).WriteToLog() } }() diff --git a/transport/internet/happy_eyeballs.go b/transport/internet/happy_eyeballs.go deleted file mode 100644 index 10adb235..00000000 --- a/transport/internet/happy_eyeballs.go +++ /dev/null @@ -1,171 +0,0 @@ -package internet - -import ( - "context" - "github.com/xtls/xray-core/common/net" - "time" -) - -type result struct { - err error - conn net.Conn - index int -} - -func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Port, sockopt *SocketConfig) (net.Conn, error) { - if len(ips) < 2 { - panic("at least 2 ips is required to race dial") - } - - prioritizeIPv6 := sockopt.HappyEyeballs.PrioritizeIpv6 - interleave := sockopt.HappyEyeballs.Interleave - tryDelayMs := time.Duration(sockopt.HappyEyeballs.TryDelayMs) * time.Millisecond - maxConcurrentTry := sockopt.HappyEyeballs.MaxConcurrentTry - - ips = sortIPs(ips, prioritizeIPv6, interleave) - newCtx, cancel := context.WithCancel(ctx) - defer cancel() - var resultCh = make(chan *result, len(ips)) - nextTryIndex := 0 - activeNum := uint32(0) - timer := time.NewTimer(0) - var winConn net.Conn - for { - select { - case r := <-resultCh: - activeNum-- - select { - case <-ctx.Done(): - cancel() - timer.Stop() - if winConn != nil { - winConn.Close() - } - if r.conn != nil { - r.conn.Close() - } - if activeNum == 0 { - return nil, ctx.Err() - } - continue - default: - if r.conn != nil { - cancel() - timer.Stop() - if winConn == nil { - winConn = r.conn - } else { - r.conn.Close() - } - } - if winConn != nil && activeNum == 0 { - return winConn, nil - } - if winConn != nil { - continue - } - if nextTryIndex < len(ips) { - timer.Reset(0) - continue - } - if activeNum == 0 { - return nil, r.err - } - timer.Stop() - continue - } - - case <-timer.C: - if nextTryIndex == len(ips) || activeNum == maxConcurrentTry { - panic("impossible situation") - } - go tcpTryDial(newCtx, src, sockopt, ips[nextTryIndex], port, nextTryIndex, resultCh) - activeNum++ - nextTryIndex++ - if nextTryIndex == len(ips) || activeNum == maxConcurrentTry { - timer.Stop() - } else { - timer.Reset(tryDelayMs) - } - continue - } - } -} - -// sortIPs sort IPs according to rfc 8305. -func sortIPs(ips []net.IP, prioritizeIPv6 bool, interleave uint32) []net.IP { - if len(ips) == 0 { - return ips - } - var ip4 = make([]net.IP, 0, len(ips)) - var ip6 = make([]net.IP, 0, len(ips)) - for _, ip := range ips { - parsedIp := net.IPAddress(ip).IP() - if len(parsedIp) == net.IPv4len { - ip4 = append(ip4, parsedIp) - } else { - ip6 = append(ip6, parsedIp) - } - } - - if len(ip4) == 0 || len(ip6) == 0 { - return ips - } - - var newIPs = make([]net.IP, 0, len(ips)) - consumeIP4 := 0 - consumeIP6 := 0 - consumeTurn := uint32(0) - ip4turn := true - if prioritizeIPv6 { - ip4turn = false - } - for { - if ip4turn { - newIPs = append(newIPs, ip4[consumeIP4]) - consumeIP4++ - if consumeIP4 == len(ip4) { - newIPs = append(newIPs, ip6[consumeIP6:]...) - break - } - consumeTurn++ - if consumeTurn == interleave { - ip4turn = false - consumeTurn = uint32(0) - } - } else { - newIPs = append(newIPs, ip6[consumeIP6]) - consumeIP6++ - if consumeIP6 == len(ip6) { - newIPs = append(newIPs, ip4[consumeIP4:]...) - break - } - consumeTurn++ - if consumeTurn == interleave { - ip4turn = true - consumeTurn = uint32(0) - } - } - } - - return newIPs -} - -func tcpTryDial(ctx context.Context, src net.Address, sockopt *SocketConfig, ip net.IP, port net.Port, index int, resultCh chan<- *result) { - conn, err := effectiveSystemDialer.Dial(ctx, src, net.Destination{Address: net.IPAddress(ip), Network: net.Network_TCP, Port: port}, sockopt) - select { - case <-ctx.Done(): - if conn != nil { - conn.Close() - } - resultCh <- &result{err: ctx.Err(), index: index} - return - default: - if err != nil { - resultCh <- &result{err: err, index: index} - return - } - resultCh <- &result{conn: conn, index: index} - return - } -} diff --git a/transport/internet/header.go b/transport/internet/header.go index 95c76343..1fc67133 100644 --- a/transport/internet/header.go +++ b/transport/internet/header.go @@ -5,7 +5,6 @@ import ( "net" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" ) type PacketHeader interface { @@ -21,7 +20,7 @@ func CreatePacketHeader(config interface{}) (PacketHeader, error) { if h, ok := header.(PacketHeader); ok { return h, nil } - return nil, errors.New("not a packet header") + return nil, newError("not a packet header") } type ConnectionAuthenticator interface { @@ -37,5 +36,5 @@ func CreateConnectionAuthenticator(config interface{}) (ConnectionAuthenticator, if a, ok := auth.(ConnectionAuthenticator); ok { return a, nil } - return nil, errors.New("not a ConnectionAuthenticator") + return nil, newError("not a ConnectionAuthenticator") } diff --git a/transport/internet/headers/dns/config.pb.go b/transport/internet/headers/dns/config.pb.go index ee522b1f..d42f537e 100644 --- a/transport/internet/headers/dns/config.pb.go +++ b/transport/internet/headers/dns/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/dns/config.proto package dns @@ -30,9 +30,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_headers_dns_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_dns_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -43,7 +45,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_dns_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -100,7 +102,7 @@ func file_transport_internet_headers_dns_config_proto_rawDescGZIP() []byte { } var file_transport_internet_headers_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_headers_dns_config_proto_goTypes = []any{ +var file_transport_internet_headers_dns_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.transport.internet.headers.dns.Config } var file_transport_internet_headers_dns_config_proto_depIdxs = []int32{ @@ -116,6 +118,20 @@ func file_transport_internet_headers_dns_config_proto_init() { if File_transport_internet_headers_dns_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_dns_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/headers/dns/dns.go b/transport/internet/headers/dns/dns.go index b6345213..5839bc81 100644 --- a/transport/internet/headers/dns/dns.go +++ b/transport/internet/headers/dns/dns.go @@ -3,8 +3,8 @@ package dns import ( "context" "encoding/binary" - "errors" + "github.com/miekg/dns" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/dice" ) @@ -36,7 +36,8 @@ func NewDNS(ctx context.Context, config interface{}) (interface{}, error) { buf := make([]byte, 0x100) - off1, err := packDomainName(config.(*Config).Domain+".", buf) + off1, err := dns.PackDomainName(dns.Fqdn(config.(*Config).Domain), buf, 0, nil, false) + if err != nil { return nil, err } @@ -51,73 +52,6 @@ func NewDNS(ctx context.Context, config interface{}) (interface{}, error) { }, nil } -// copied from github.com/miekg/dns -func packDomainName(s string, msg []byte) (off1 int, err error) { - off := 0 - ls := len(s) - // Each dot ends a segment of the name. - // We trade each dot byte for a length byte. - // Except for escaped dots (\.), which are normal dots. - // There is also a trailing zero. - - // Emit sequence of counted strings, chopping at dots. - var ( - begin int - bs []byte - ) - for i := 0; i < ls; i++ { - var c byte - if bs == nil { - c = s[i] - } else { - c = bs[i] - } - - switch c { - case '\\': - if off+1 > len(msg) { - return len(msg), errors.New("buffer size too small") - } - - if bs == nil { - bs = []byte(s) - } - - copy(bs[i:ls-1], bs[i+1:]) - ls-- - case '.': - labelLen := i - begin - if labelLen >= 1<<6 { // top two bits of length must be clear - return len(msg), errors.New("bad rdata") - } - - // off can already (we're in a loop) be bigger than len(msg) - // this happens when a name isn't fully qualified - if off+1+labelLen > len(msg) { - return len(msg), errors.New("buffer size too small") - } - - // The following is covered by the length check above. - msg[off] = byte(labelLen) - - if bs == nil { - copy(msg[off+1:], s[begin:i]) - } else { - copy(msg[off+1:], bs[begin:i]) - } - off += 1 + labelLen - begin = i + 1 - default: - } - } - - if off < len(msg) { - msg[off] = 0 - } - - return off + 1, nil -} - func init() { common.Must(common.RegisterConfig((*Config)(nil), NewDNS)) } diff --git a/transport/internet/headers/http/config.pb.go b/transport/internet/headers/http/config.pb.go index 49a0caa5..382ced84 100644 --- a/transport/internet/headers/http/config.pb.go +++ b/transport/internet/headers/http/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/http/config.proto package http @@ -34,9 +34,11 @@ type Header struct { func (x *Header) Reset() { *x = Header{} - mi := &file_transport_internet_headers_http_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_http_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Header) String() string { @@ -47,7 +49,7 @@ func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -87,9 +89,11 @@ type Version struct { func (x *Version) Reset() { *x = Version{} - mi := &file_transport_internet_headers_http_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_http_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Version) String() string { @@ -100,7 +104,7 @@ func (*Version) ProtoMessage() {} func (x *Version) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -133,9 +137,11 @@ type Method struct { func (x *Method) Reset() { *x = Method{} - mi := &file_transport_internet_headers_http_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_http_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Method) String() string { @@ -146,7 +152,7 @@ func (*Method) ProtoMessage() {} func (x *Method) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -184,9 +190,11 @@ type RequestConfig struct { func (x *RequestConfig) Reset() { *x = RequestConfig{} - mi := &file_transport_internet_headers_http_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_http_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RequestConfig) String() string { @@ -197,7 +205,7 @@ func (*RequestConfig) ProtoMessage() {} func (x *RequestConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -253,9 +261,11 @@ type Status struct { func (x *Status) Reset() { *x = Status{} - mi := &file_transport_internet_headers_http_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_http_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Status) String() string { @@ -266,7 +276,7 @@ func (*Status) ProtoMessage() {} func (x *Status) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -307,9 +317,11 @@ type ResponseConfig struct { func (x *ResponseConfig) Reset() { *x = ResponseConfig{} - mi := &file_transport_internet_headers_http_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_http_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ResponseConfig) String() string { @@ -320,7 +332,7 @@ func (*ResponseConfig) ProtoMessage() {} func (x *ResponseConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -362,7 +374,7 @@ type Config struct { unknownFields protoimpl.UnknownFields // Settings for authenticating requests. If not set, client side will not send - // authentication header, and server side will bypass authentication. + // authenication header, and server side will bypass authentication. Request *RequestConfig `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // Settings for authenticating responses. If not set, client side will bypass // authentication, and server side will not send authentication header. @@ -371,9 +383,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_headers_http_config_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_http_config_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -384,7 +398,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -498,7 +512,7 @@ func file_transport_internet_headers_http_config_proto_rawDescGZIP() []byte { } var file_transport_internet_headers_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_transport_internet_headers_http_config_proto_goTypes = []any{ +var file_transport_internet_headers_http_config_proto_goTypes = []interface{}{ (*Header)(nil), // 0: xray.transport.internet.headers.http.Header (*Version)(nil), // 1: xray.transport.internet.headers.http.Version (*Method)(nil), // 2: xray.transport.internet.headers.http.Method @@ -528,6 +542,92 @@ func file_transport_internet_headers_http_config_proto_init() { if File_transport_internet_headers_http_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_http_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Header); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_headers_http_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Version); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_headers_http_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Method); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_headers_http_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_headers_http_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Status); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_headers_http_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResponseConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_headers_http_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/headers/http/config.proto b/transport/internet/headers/http/config.proto index fd5799c4..c7659371 100644 --- a/transport/internet/headers/http/config.proto +++ b/transport/internet/headers/http/config.proto @@ -56,7 +56,7 @@ message ResponseConfig { message Config { // Settings for authenticating requests. If not set, client side will not send - // authentication header, and server side will bypass authentication. + // authenication header, and server side will bypass authentication. RequestConfig request = 1; // Settings for authenticating responses. If not set, client side will bypass diff --git a/transport/internet/headers/http/errors.generated.go b/transport/internet/headers/http/errors.generated.go new file mode 100644 index 00000000..f0048165 --- /dev/null +++ b/transport/internet/headers/http/errors.generated.go @@ -0,0 +1,9 @@ +package http + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/headers/http/http.go b/transport/internet/headers/http/http.go index 3c1ff06c..b9afb230 100644 --- a/transport/internet/headers/http/http.go +++ b/transport/internet/headers/http/http.go @@ -1,5 +1,7 @@ package http +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + import ( "bufio" "bytes" @@ -12,7 +14,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" ) const ( @@ -27,9 +28,9 @@ const ( ) var ( - ErrHeaderToLong = errors.New("Header too long.") + ErrHeaderToLong = newError("Header too long.") - ErrHeaderMisMatch = errors.New("Header Mismatch.") + ErrHeaderMisMatch = newError("Header Mismatch.") ) type Reader interface { diff --git a/transport/internet/headers/noop/config.pb.go b/transport/internet/headers/noop/config.pb.go index f1756060..534b3701 100644 --- a/transport/internet/headers/noop/config.pb.go +++ b/transport/internet/headers/noop/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/noop/config.proto package noop @@ -28,9 +28,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_headers_noop_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_noop_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -41,7 +43,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_noop_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -64,9 +66,11 @@ type ConnectionConfig struct { func (x *ConnectionConfig) Reset() { *x = ConnectionConfig{} - mi := &file_transport_internet_headers_noop_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_noop_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ConnectionConfig) String() string { @@ -77,7 +81,7 @@ func (*ConnectionConfig) ProtoMessage() {} func (x *ConnectionConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_noop_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -127,7 +131,7 @@ func file_transport_internet_headers_noop_config_proto_rawDescGZIP() []byte { } var file_transport_internet_headers_noop_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_transport_internet_headers_noop_config_proto_goTypes = []any{ +var file_transport_internet_headers_noop_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.transport.internet.headers.noop.Config (*ConnectionConfig)(nil), // 1: xray.transport.internet.headers.noop.ConnectionConfig } @@ -144,6 +148,32 @@ func file_transport_internet_headers_noop_config_proto_init() { if File_transport_internet_headers_noop_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_noop_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_headers_noop_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectionConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/headers/srtp/config.pb.go b/transport/internet/headers/srtp/config.pb.go index 81728497..5c0f9cff 100644 --- a/transport/internet/headers/srtp/config.pb.go +++ b/transport/internet/headers/srtp/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/srtp/config.proto package srtp @@ -35,9 +35,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_headers_srtp_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_srtp_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -48,7 +50,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_srtp_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -150,7 +152,7 @@ func file_transport_internet_headers_srtp_config_proto_rawDescGZIP() []byte { } var file_transport_internet_headers_srtp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_headers_srtp_config_proto_goTypes = []any{ +var file_transport_internet_headers_srtp_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.transport.internet.headers.srtp.Config } var file_transport_internet_headers_srtp_config_proto_depIdxs = []int32{ @@ -166,6 +168,20 @@ func file_transport_internet_headers_srtp_config_proto_init() { if File_transport_internet_headers_srtp_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_srtp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/headers/tls/config.pb.go b/transport/internet/headers/tls/config.pb.go index c67ee426..7867f69c 100644 --- a/transport/internet/headers/tls/config.pb.go +++ b/transport/internet/headers/tls/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/tls/config.proto package tls @@ -28,9 +28,11 @@ type PacketConfig struct { func (x *PacketConfig) Reset() { *x = PacketConfig{} - mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *PacketConfig) String() string { @@ -41,7 +43,7 @@ func (*PacketConfig) ProtoMessage() {} func (x *PacketConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -90,7 +92,7 @@ func file_transport_internet_headers_tls_config_proto_rawDescGZIP() []byte { } var file_transport_internet_headers_tls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_headers_tls_config_proto_goTypes = []any{ +var file_transport_internet_headers_tls_config_proto_goTypes = []interface{}{ (*PacketConfig)(nil), // 0: xray.transport.internet.headers.tls.PacketConfig } var file_transport_internet_headers_tls_config_proto_depIdxs = []int32{ @@ -106,6 +108,20 @@ func file_transport_internet_headers_tls_config_proto_init() { if File_transport_internet_headers_tls_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_tls_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PacketConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/headers/utp/config.pb.go b/transport/internet/headers/utp/config.pb.go index b0ffa60c..c7658b0b 100644 --- a/transport/internet/headers/utp/config.pb.go +++ b/transport/internet/headers/utp/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/utp/config.proto package utp @@ -30,9 +30,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_headers_utp_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_utp_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -43,7 +45,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_utp_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -100,7 +102,7 @@ func file_transport_internet_headers_utp_config_proto_rawDescGZIP() []byte { } var file_transport_internet_headers_utp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_headers_utp_config_proto_goTypes = []any{ +var file_transport_internet_headers_utp_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.transport.internet.headers.utp.Config } var file_transport_internet_headers_utp_config_proto_depIdxs = []int32{ @@ -116,6 +118,20 @@ func file_transport_internet_headers_utp_config_proto_init() { if File_transport_internet_headers_utp_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_utp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/headers/wechat/config.pb.go b/transport/internet/headers/wechat/config.pb.go index 48970394..1b624b59 100644 --- a/transport/internet/headers/wechat/config.pb.go +++ b/transport/internet/headers/wechat/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/wechat/config.proto package wechat @@ -28,9 +28,11 @@ type VideoConfig struct { func (x *VideoConfig) Reset() { *x = VideoConfig{} - mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *VideoConfig) String() string { @@ -41,7 +43,7 @@ func (*VideoConfig) ProtoMessage() {} func (x *VideoConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -91,7 +93,7 @@ func file_transport_internet_headers_wechat_config_proto_rawDescGZIP() []byte { } var file_transport_internet_headers_wechat_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_headers_wechat_config_proto_goTypes = []any{ +var file_transport_internet_headers_wechat_config_proto_goTypes = []interface{}{ (*VideoConfig)(nil), // 0: xray.transport.internet.headers.wechat.VideoConfig } var file_transport_internet_headers_wechat_config_proto_depIdxs = []int32{ @@ -107,6 +109,20 @@ func file_transport_internet_headers_wechat_config_proto_init() { if File_transport_internet_headers_wechat_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_wechat_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VideoConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/headers/wireguard/config.pb.go b/transport/internet/headers/wireguard/config.pb.go index 3746c1aa..8d3be7a1 100644 --- a/transport/internet/headers/wireguard/config.pb.go +++ b/transport/internet/headers/wireguard/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/headers/wireguard/config.proto package wireguard @@ -28,9 +28,11 @@ type WireguardConfig struct { func (x *WireguardConfig) Reset() { *x = WireguardConfig{} - mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *WireguardConfig) String() string { @@ -41,7 +43,7 @@ func (*WireguardConfig) ProtoMessage() {} func (x *WireguardConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -92,7 +94,7 @@ func file_transport_internet_headers_wireguard_config_proto_rawDescGZIP() []byte } var file_transport_internet_headers_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_headers_wireguard_config_proto_goTypes = []any{ +var file_transport_internet_headers_wireguard_config_proto_goTypes = []interface{}{ (*WireguardConfig)(nil), // 0: xray.transport.internet.headers.wireguard.WireguardConfig } var file_transport_internet_headers_wireguard_config_proto_depIdxs = []int32{ @@ -108,6 +110,20 @@ func file_transport_internet_headers_wireguard_config_proto_init() { if File_transport_internet_headers_wireguard_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_wireguard_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WireguardConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/http/config.go b/transport/internet/http/config.go new file mode 100644 index 00000000..cfcc8c08 --- /dev/null +++ b/transport/internet/http/config.go @@ -0,0 +1,47 @@ +package http + +import ( + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/dice" + "github.com/xtls/xray-core/transport/internet" +) + +const protocolName = "http" + +func (c *Config) getHosts() []string { + if len(c.Host) == 0 { + return []string{"www.example.com"} + } + return c.Host +} + +func (c *Config) isValidHost(host string) bool { + hosts := c.getHosts() + for _, h := range hosts { + if h == host { + return true + } + } + return false +} + +func (c *Config) getRandomHost() string { + hosts := c.getHosts() + return hosts[dice.Roll(len(hosts))] +} + +func (c *Config) getNormalizedPath() string { + if c.Path == "" { + return "/" + } + if c.Path[0] != '/' { + return "/" + c.Path + } + return c.Path +} + +func init() { + common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { + return new(Config) + })) +} diff --git a/transport/internet/http/config.pb.go b/transport/internet/http/config.pb.go new file mode 100644 index 00000000..f87a59e8 --- /dev/null +++ b/transport/internet/http/config.pb.go @@ -0,0 +1,209 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: transport/internet/http/config.proto + +package http + +import ( + http "github.com/xtls/xray-core/transport/internet/headers/http" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + IdleTimeout int32 `protobuf:"varint,3,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` + HealthCheckTimeout int32 `protobuf:"varint,4,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` + Method string `protobuf:"bytes,5,opt,name=method,proto3" json:"method,omitempty"` + Header []*http.Header `protobuf:"bytes,6,rep,name=header,proto3" json:"header,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_http_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_http_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Config.ProtoReflect.Descriptor instead. +func (*Config) Descriptor() ([]byte, []int) { + return file_transport_internet_http_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetHost() []string { + if x != nil { + return x.Host + } + return nil +} + +func (x *Config) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Config) GetIdleTimeout() int32 { + if x != nil { + return x.IdleTimeout + } + return 0 +} + +func (x *Config) GetHealthCheckTimeout() int32 { + if x != nil { + return x.HealthCheckTimeout + } + return 0 +} + +func (x *Config) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *Config) GetHeader() []*http.Header { + if x != nil { + return x.Header + } + return nil +} + +var File_transport_internet_http_config_proto protoreflect.FileDescriptor + +var file_transport_internet_http_config_proto_rawDesc = []byte{ + 0x0a, 0x24, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, + 0x68, 0x74, 0x74, 0x70, 0x1a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, + 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xe3, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69, 0x64, 0x6c, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x12, 0x44, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2c, 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, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x76, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 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, 0x68, 0x74, 0x74, 0x70, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, + 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, + 0x70, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_transport_internet_http_config_proto_rawDescOnce sync.Once + file_transport_internet_http_config_proto_rawDescData = file_transport_internet_http_config_proto_rawDesc +) + +func file_transport_internet_http_config_proto_rawDescGZIP() []byte { + file_transport_internet_http_config_proto_rawDescOnce.Do(func() { + file_transport_internet_http_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_http_config_proto_rawDescData) + }) + return file_transport_internet_http_config_proto_rawDescData +} + +var file_transport_internet_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_http_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.transport.internet.http.Config + (*http.Header)(nil), // 1: xray.transport.internet.headers.http.Header +} +var file_transport_internet_http_config_proto_depIdxs = []int32{ + 1, // 0: xray.transport.internet.http.Config.header:type_name -> xray.transport.internet.headers.http.Header + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_transport_internet_http_config_proto_init() } +func file_transport_internet_http_config_proto_init() { + if File_transport_internet_http_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_transport_internet_http_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_transport_internet_http_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_http_config_proto_goTypes, + DependencyIndexes: file_transport_internet_http_config_proto_depIdxs, + MessageInfos: file_transport_internet_http_config_proto_msgTypes, + }.Build() + File_transport_internet_http_config_proto = out.File + file_transport_internet_http_config_proto_rawDesc = nil + file_transport_internet_http_config_proto_goTypes = nil + file_transport_internet_http_config_proto_depIdxs = nil +} diff --git a/transport/internet/http/config.proto b/transport/internet/http/config.proto new file mode 100644 index 00000000..82f5cbeb --- /dev/null +++ b/transport/internet/http/config.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package xray.transport.internet.http; +option csharp_namespace = "Xray.Transport.Internet.Http"; +option go_package = "github.com/xtls/xray-core/transport/internet/http"; +option java_package = "com.xray.transport.internet.http"; +option java_multiple_files = true; + +import "transport/internet/headers/http/config.proto"; + +message Config { + repeated string host = 1; + string path = 2; + int32 idle_timeout = 3; + int32 health_check_timeout = 4; + string method = 5; + repeated xray.transport.internet.headers.http.Header header = 6; +} diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go new file mode 100644 index 00000000..010a95a5 --- /dev/null +++ b/transport/internet/http/dialer.go @@ -0,0 +1,234 @@ +package http + +import ( + "context" + gotls "crypto/tls" + "io" + "net/http" + "net/url" + "sync" + "time" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/net/cnc" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/reality" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" + "github.com/xtls/xray-core/transport/pipe" + "golang.org/x/net/http2" +) + +type dialerConf struct { + net.Destination + *internet.MemoryStreamConfig +} + +var ( + globalDialerMap map[dialerConf]*http.Client + globalDialerAccess sync.Mutex +) + +func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*http.Client, error) { + globalDialerAccess.Lock() + defer globalDialerAccess.Unlock() + + if globalDialerMap == nil { + globalDialerMap = make(map[dialerConf]*http.Client) + } + + httpSettings := streamSettings.ProtocolSettings.(*Config) + tlsConfigs := tls.ConfigFromStreamSettings(streamSettings) + realityConfigs := reality.ConfigFromStreamSettings(streamSettings) + if tlsConfigs == nil && realityConfigs == nil { + return nil, newError("TLS or REALITY must be enabled for http transport.").AtWarning() + } + sockopt := streamSettings.SocketSettings + + if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found { + return client, nil + } + + transport := &http2.Transport{ + DialTLS: func(network string, addr string, tlsConfig *gotls.Config) (net.Conn, error) { + rawHost, rawPort, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + if len(rawPort) == 0 { + rawPort = "443" + } + port, err := net.PortFromString(rawPort) + if err != nil { + return nil, err + } + address := net.ParseAddress(rawHost) + + dctx := context.Background() + dctx = session.ContextWithID(dctx, session.IDFromContext(ctx)) + dctx = session.ContextWithOutbound(dctx, session.OutboundFromContext(ctx)) + + pconn, err := internet.DialSystem(dctx, net.TCPDestination(address, port), sockopt) + if err != nil { + newError("failed to dial to " + addr).Base(err).AtError().WriteToLog() + return nil, err + } + + if realityConfigs != nil { + return reality.UClient(pconn, realityConfigs, ctx, dest) + } + + var cn tls.Interface + if fingerprint := tls.GetFingerprint(tlsConfigs.Fingerprint); fingerprint != nil { + cn = tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn) + } else { + cn = tls.Client(pconn, tlsConfig).(*tls.Conn) + } + if err := cn.Handshake(); err != nil { + newError("failed to dial to " + addr).Base(err).AtError().WriteToLog() + return nil, err + } + if !tlsConfig.InsecureSkipVerify { + if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil { + newError("failed to dial to " + addr).Base(err).AtError().WriteToLog() + return nil, err + } + } + negotiatedProtocol, negotiatedProtocolIsMutual := cn.NegotiatedProtocol() + if negotiatedProtocol != http2.NextProtoTLS { + return nil, newError("http2: unexpected ALPN protocol " + negotiatedProtocol + "; want q" + http2.NextProtoTLS).AtError() + } + if !negotiatedProtocolIsMutual { + return nil, newError("http2: could not negotiate protocol mutually").AtError() + } + return cn, nil + }, + } + + if tlsConfigs != nil { + transport.TLSClientConfig = tlsConfigs.GetTLSConfig(tls.WithDestination(dest)) + } + + if httpSettings.IdleTimeout > 0 || httpSettings.HealthCheckTimeout > 0 { + transport.ReadIdleTimeout = time.Second * time.Duration(httpSettings.IdleTimeout) + transport.PingTimeout = time.Second * time.Duration(httpSettings.HealthCheckTimeout) + } + + client := &http.Client{ + Transport: transport, + } + + globalDialerMap[dialerConf{dest, streamSettings}] = client + return client, nil +} + +// Dial dials a new TCP connection to the given destination. +func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { + httpSettings := streamSettings.ProtocolSettings.(*Config) + client, err := getHTTPClient(ctx, dest, streamSettings) + if err != nil { + return nil, err + } + + opts := pipe.OptionsFromContext(ctx) + preader, pwriter := pipe.New(opts...) + breader := &buf.BufferedReader{Reader: preader} + + httpMethod := "PUT" + if httpSettings.Method != "" { + httpMethod = httpSettings.Method + } + + httpHeaders := make(http.Header) + + for _, httpHeader := range httpSettings.Header { + for _, httpHeaderValue := range httpHeader.Value { + httpHeaders.Set(httpHeader.Name, httpHeaderValue) + } + } + + request := &http.Request{ + Method: httpMethod, + Host: httpSettings.getRandomHost(), + Body: breader, + URL: &url.URL{ + Scheme: "https", + Host: dest.NetAddr(), + Path: httpSettings.getNormalizedPath(), + }, + Proto: "HTTP/2", + ProtoMajor: 2, + ProtoMinor: 0, + Header: httpHeaders, + } + // Disable any compression method from server. + request.Header.Set("Accept-Encoding", "identity") + + wrc := &WaitReadCloser{Wait: make(chan struct{})} + go func() { + response, err := client.Do(request) + if err != nil { + newError("failed to dial to ", dest).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) + wrc.Close() + return + } + if response.StatusCode != 200 { + newError("unexpected status", response.StatusCode).AtWarning().WriteToLog(session.ExportIDToError(ctx)) + wrc.Close() + return + } + wrc.Set(response.Body) + }() + + bwriter := buf.NewBufferedWriter(pwriter) + common.Must(bwriter.SetBuffered(false)) + return cnc.NewConnection( + cnc.ConnectionOutput(wrc), + cnc.ConnectionInput(bwriter), + cnc.ConnectionOnClose(common.ChainedClosable{breader, bwriter, wrc}), + ), nil +} + +func init() { + common.Must(internet.RegisterTransportDialer(protocolName, Dial)) +} + +type WaitReadCloser struct { + Wait chan struct{} + io.ReadCloser +} + +func (w *WaitReadCloser) Set(rc io.ReadCloser) { + w.ReadCloser = rc + defer func() { + if recover() != nil { + rc.Close() + } + }() + close(w.Wait) +} + +func (w *WaitReadCloser) Read(b []byte) (int, error) { + if w.ReadCloser == nil { + if <-w.Wait; w.ReadCloser == nil { + return 0, io.ErrClosedPipe + } + } + return w.ReadCloser.Read(b) +} + +func (w *WaitReadCloser) Close() error { + if w.ReadCloser != nil { + return w.ReadCloser.Close() + } + defer func() { + if recover() != nil && w.ReadCloser != nil { + w.ReadCloser.Close() + } + }() + close(w.Wait) + return nil +} diff --git a/transport/internet/http/errors.generated.go b/transport/internet/http/errors.generated.go new file mode 100644 index 00000000..f0048165 --- /dev/null +++ b/transport/internet/http/errors.generated.go @@ -0,0 +1,9 @@ +package http + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/http/http.go b/transport/internet/http/http.go new file mode 100644 index 00000000..3c0d015f --- /dev/null +++ b/transport/internet/http/http.go @@ -0,0 +1,3 @@ +package http + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/http/http_test.go b/transport/internet/http/http_test.go new file mode 100644 index 00000000..3639eb84 --- /dev/null +++ b/transport/internet/http/http_test.go @@ -0,0 +1,94 @@ +package http_test + +import ( + "context" + "crypto/rand" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/protocol/tls/cert" + "github.com/xtls/xray-core/testing/servers/tcp" + "github.com/xtls/xray-core/transport/internet" + . "github.com/xtls/xray-core/transport/internet/http" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" +) + +func TestHTTPConnection(t *testing.T) { + port := tcp.PickPort() + + listener, err := Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ + ProtocolName: "http", + ProtocolSettings: &Config{}, + SecurityType: "tls", + SecuritySettings: &tls.Config{ + Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("www.example.com")))}, + }, + }, func(conn stat.Connection) { + go func() { + defer conn.Close() + + b := buf.New() + defer b.Release() + + for { + if _, err := b.ReadFrom(conn); err != nil { + return + } + _, err := conn.Write(b.Bytes()) + common.Must(err) + } + }() + }) + common.Must(err) + + defer listener.Close() + + time.Sleep(time.Second) + + dctx := context.Background() + conn, err := Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ + ProtocolName: "http", + ProtocolSettings: &Config{}, + SecurityType: "tls", + SecuritySettings: &tls.Config{ + ServerName: "www.example.com", + AllowInsecure: true, + }, + }) + common.Must(err) + defer conn.Close() + + const N = 1024 + b1 := make([]byte, N) + common.Must2(rand.Read(b1)) + b2 := buf.New() + + nBytes, err := conn.Write(b1) + common.Must(err) + if nBytes != N { + t.Error("write: ", nBytes) + } + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } + + nBytes, err = conn.Write(b1) + common.Must(err) + if nBytes != N { + t.Error("write: ", nBytes) + } + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } +} diff --git a/transport/internet/http/hub.go b/transport/internet/http/hub.go new file mode 100644 index 00000000..551f897e --- /dev/null +++ b/transport/internet/http/hub.go @@ -0,0 +1,219 @@ +package http + +import ( + "context" + "io" + "net/http" + "strings" + "time" + + goreality "github.com/xtls/reality" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/net/cnc" + http_proto "github.com/xtls/xray-core/common/protocol/http" + "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/signal/done" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/reality" + "github.com/xtls/xray-core/transport/internet/tls" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" +) + +type Listener struct { + server *http.Server + handler internet.ConnHandler + local net.Addr + config *Config + locker *internet.FileLocker // for unix domain socket +} + +func (l *Listener) Addr() net.Addr { + return l.local +} + +func (l *Listener) Close() error { + if l.locker != nil { + l.locker.Release() + } + return l.server.Close() +} + +type flushWriter struct { + w io.Writer + d *done.Instance +} + +func (fw flushWriter) Write(p []byte) (n int, err error) { + if fw.d.Done() { + return 0, io.ErrClosedPipe + } + + defer func() { + if recover() != nil { + fw.d.Close() + err = io.ErrClosedPipe + } + }() + + n, err = fw.w.Write(p) + if f, ok := fw.w.(http.Flusher); ok && err == nil { + f.Flush() + } + return +} + +func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + host := request.Host + if !l.config.isValidHost(host) { + writer.WriteHeader(404) + return + } + path := l.config.getNormalizedPath() + if !strings.HasPrefix(request.URL.Path, path) { + writer.WriteHeader(404) + return + } + + writer.Header().Set("Cache-Control", "no-store") + + for _, httpHeader := range l.config.Header { + for _, httpHeaderValue := range httpHeader.Value { + writer.Header().Set(httpHeader.Name, httpHeaderValue) + } + } + + writer.WriteHeader(200) + if f, ok := writer.(http.Flusher); ok { + f.Flush() + } + + remoteAddr := l.Addr() + dest, err := net.ParseDestination(request.RemoteAddr) + if err != nil { + newError("failed to parse request remote addr: ", request.RemoteAddr).Base(err).WriteToLog() + } else { + remoteAddr = &net.TCPAddr{ + IP: dest.Address.IP(), + Port: int(dest.Port), + } + } + + forwardedAddress := http_proto.ParseXForwardedFor(request.Header) + if len(forwardedAddress) > 0 && forwardedAddress[0].Family().IsIP() { + remoteAddr = &net.TCPAddr{ + IP: forwardedAddress[0].IP(), + Port: 0, + } + } + + done := done.New() + conn := cnc.NewConnection( + cnc.ConnectionOutput(request.Body), + cnc.ConnectionInput(flushWriter{w: writer, d: done}), + cnc.ConnectionOnClose(common.ChainedClosable{done, request.Body}), + cnc.ConnectionLocalAddr(l.Addr()), + cnc.ConnectionRemoteAddr(remoteAddr), + ) + l.handler(conn) + <-done.Wait() +} + +func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { + httpSettings := streamSettings.ProtocolSettings.(*Config) + var listener *Listener + if port == net.Port(0) { // unix + listener = &Listener{ + handler: handler, + local: &net.UnixAddr{ + Name: address.Domain(), + Net: "unix", + }, + config: httpSettings, + } + } else { // tcp + listener = &Listener{ + handler: handler, + local: &net.TCPAddr{ + IP: address.IP(), + Port: int(port), + }, + config: httpSettings, + } + } + + var server *http.Server + config := tls.ConfigFromStreamSettings(streamSettings) + if config == nil { + h2s := &http2.Server{} + + server = &http.Server{ + Addr: serial.Concat(address, ":", port), + Handler: h2c.NewHandler(listener, h2s), + ReadHeaderTimeout: time.Second * 4, + } + } else { + server = &http.Server{ + Addr: serial.Concat(address, ":", port), + TLSConfig: config.GetTLSConfig(tls.WithNextProto("h2")), + Handler: listener, + ReadHeaderTimeout: time.Second * 4, + } + } + + if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { + newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) + } + + listener.server = server + go func() { + var streamListener net.Listener + var err error + if port == net.Port(0) { // unix + streamListener, err = internet.ListenSystem(ctx, &net.UnixAddr{ + Name: address.Domain(), + Net: "unix", + }, streamSettings.SocketSettings) + if err != nil { + newError("failed to listen on ", address).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) + return + } + locker := ctx.Value(address.Domain()) + if locker != nil { + listener.locker = locker.(*internet.FileLocker) + } + } else { // tcp + streamListener, err = internet.ListenSystem(ctx, &net.TCPAddr{ + IP: address.IP(), + Port: int(port), + }, streamSettings.SocketSettings) + if err != nil { + newError("failed to listen on ", address, ":", port).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) + return + } + } + + if config == nil { + if config := reality.ConfigFromStreamSettings(streamSettings); config != nil { + streamListener = goreality.NewListener(streamListener, config.GetREALITYConfig()) + } + err = server.Serve(streamListener) + if err != nil { + newError("stopping serving H2C or REALITY H2").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + } else { + err = server.ServeTLS(streamListener, "", "") + if err != nil { + newError("stopping serving TLS H2").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + } + }() + + return listener, nil +} + +func init() { + common.Must(internet.RegisterTransportListener(protocolName, Listen)) +} diff --git a/transport/internet/httpupgrade/config.go b/transport/internet/httpupgrade/config.go deleted file mode 100644 index 3404930e..00000000 --- a/transport/internet/httpupgrade/config.go +++ /dev/null @@ -1,23 +0,0 @@ -package httpupgrade - -import ( - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/transport/internet" -) - -func (c *Config) GetNormalizedPath() string { - path := c.Path - if path == "" { - return "/" - } - if path[0] != '/' { - return "/" + path - } - return path -} - -func init() { - common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { - return new(Config) - })) -} diff --git a/transport/internet/httpupgrade/config.pb.go b/transport/internet/httpupgrade/config.pb.go deleted file mode 100644 index 72baaf8b..00000000 --- a/transport/internet/httpupgrade/config.pb.go +++ /dev/null @@ -1,185 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/httpupgrade/config.proto - -package httpupgrade - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - Header map[string]string `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - AcceptProxyProtocol bool `protobuf:"varint,4,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` - Ed uint32 `protobuf:"varint,5,opt,name=ed,proto3" json:"ed,omitempty"` -} - -func (x *Config) Reset() { - *x = Config{} - mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Config) ProtoMessage() {} - -func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Config.ProtoReflect.Descriptor instead. -func (*Config) Descriptor() ([]byte, []int) { - return file_transport_internet_httpupgrade_config_proto_rawDescGZIP(), []int{0} -} - -func (x *Config) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *Config) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *Config) GetHeader() map[string]string { - if x != nil { - return x.Header - } - return nil -} - -func (x *Config) GetAcceptProxyProtocol() bool { - if x != nil { - return x.AcceptProxyProtocol - } - return false -} - -func (x *Config) GetEd() uint32 { - if x != nil { - return x.Ed - } - return 0 -} - -var File_transport_internet_httpupgrade_config_proto protoreflect.FileDescriptor - -var file_transport_internet_httpupgrade_config_proto_rawDesc = []byte{ - 0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, - 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, - 0x64, 0x65, 0x22, 0x80, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, - 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x4f, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 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, - 0x68, 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, - 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x65, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x65, 0x64, 0x1a, 0x39, 0x0a, 0x0b, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x8b, 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 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, 0x68, 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, - 0x65, 0x50, 0x01, 0x5a, 0x38, 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, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, - 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0xaa, 0x02, 0x23, - 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x55, 0x70, 0x67, 0x72, - 0x61, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_transport_internet_httpupgrade_config_proto_rawDescOnce sync.Once - file_transport_internet_httpupgrade_config_proto_rawDescData = file_transport_internet_httpupgrade_config_proto_rawDesc -) - -func file_transport_internet_httpupgrade_config_proto_rawDescGZIP() []byte { - file_transport_internet_httpupgrade_config_proto_rawDescOnce.Do(func() { - file_transport_internet_httpupgrade_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_httpupgrade_config_proto_rawDescData) - }) - return file_transport_internet_httpupgrade_config_proto_rawDescData -} - -var file_transport_internet_httpupgrade_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_transport_internet_httpupgrade_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.transport.internet.httpupgrade.Config - nil, // 1: xray.transport.internet.httpupgrade.Config.HeaderEntry -} -var file_transport_internet_httpupgrade_config_proto_depIdxs = []int32{ - 1, // 0: xray.transport.internet.httpupgrade.Config.header:type_name -> xray.transport.internet.httpupgrade.Config.HeaderEntry - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_transport_internet_httpupgrade_config_proto_init() } -func file_transport_internet_httpupgrade_config_proto_init() { - if File_transport_internet_httpupgrade_config_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_transport_internet_httpupgrade_config_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_transport_internet_httpupgrade_config_proto_goTypes, - DependencyIndexes: file_transport_internet_httpupgrade_config_proto_depIdxs, - MessageInfos: file_transport_internet_httpupgrade_config_proto_msgTypes, - }.Build() - File_transport_internet_httpupgrade_config_proto = out.File - file_transport_internet_httpupgrade_config_proto_rawDesc = nil - file_transport_internet_httpupgrade_config_proto_goTypes = nil - file_transport_internet_httpupgrade_config_proto_depIdxs = nil -} diff --git a/transport/internet/httpupgrade/config.proto b/transport/internet/httpupgrade/config.proto deleted file mode 100644 index 98206f51..00000000 --- a/transport/internet/httpupgrade/config.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.httpupgrade; -option csharp_namespace = "Xray.Transport.Internet.HttpUpgrade"; -option go_package = "github.com/xtls/xray-core/transport/internet/httpupgrade"; -option java_package = "com.xray.transport.internet.httpupgrade"; -option java_multiple_files = true; - -message Config { - string host = 1; - string path = 2; - map header = 3; - bool accept_proxy_protocol = 4; - uint32 ed = 5; -} diff --git a/transport/internet/httpupgrade/connection.go b/transport/internet/httpupgrade/connection.go deleted file mode 100644 index 1bc4d755..00000000 --- a/transport/internet/httpupgrade/connection.go +++ /dev/null @@ -1,19 +0,0 @@ -package httpupgrade - -import "net" - -type connection struct { - net.Conn - remoteAddr net.Addr -} - -func newConnection(conn net.Conn, remoteAddr net.Addr) *connection { - return &connection{ - Conn: conn, - remoteAddr: remoteAddr, - } -} - -func (c *connection) RemoteAddr() net.Addr { - return c.remoteAddr -} diff --git a/transport/internet/httpupgrade/dialer.go b/transport/internet/httpupgrade/dialer.go deleted file mode 100644 index c10bd97e..00000000 --- a/transport/internet/httpupgrade/dialer.go +++ /dev/null @@ -1,132 +0,0 @@ -package httpupgrade - -import ( - "bufio" - "context" - "net/http" - "net/url" - "strings" - - "github.com/xtls/xray-core/common" - "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" - "github.com/xtls/xray-core/transport/internet/tls" -) - -type ConnRF struct { - net.Conn - Req *http.Request - First bool -} - -func (c *ConnRF) Read(b []byte) (int, error) { - if c.First { - c.First = false - // create reader capped to size of `b`, so it can be fully drained into - // `b` later with a single Read call - reader := bufio.NewReaderSize(c.Conn, len(b)) - resp, err := http.ReadResponse(reader, c.Req) // nolint:bodyclose - if err != nil { - return 0, err - } - if resp.Status != "101 Switching Protocols" || - strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || - strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { - return 0, errors.New("unrecognized reply") - } - // drain remaining bufreader - return reader.Read(b[:reader.Buffered()]) - } - return c.Conn.Read(b) -} - -func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { - transportConfiguration := streamSettings.ProtocolSettings.(*Config) - - pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) - if err != nil { - errors.LogErrorInner(ctx, err, "failed to dial to ", dest) - return nil, err - } - - var conn net.Conn - var requestURL url.URL - tConfig := tls.ConfigFromStreamSettings(streamSettings) - if tConfig != nil { - tlsConfig := tConfig.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) - if fingerprint := tls.GetFingerprint(tConfig.Fingerprint); fingerprint != nil { - conn = tls.UClient(pconn, tlsConfig, fingerprint) - if err := conn.(*tls.UConn).WebsocketHandshakeContext(ctx); err != nil { - return nil, err - } - } else { - conn = tls.Client(pconn, tlsConfig) - } - requestURL.Scheme = "https" - } else { - conn = pconn - requestURL.Scheme = "http" - } - - requestURL.Host = transportConfiguration.Host - if requestURL.Host == "" && tConfig != nil { - requestURL.Host = tConfig.ServerName - } - if requestURL.Host == "" { - requestURL.Host = dest.Address.String() - } - requestURL.Path = transportConfiguration.GetNormalizedPath() - req := &http.Request{ - Method: http.MethodGet, - URL: &requestURL, - Header: make(http.Header), - } - for key, value := range transportConfiguration.Header { - AddHeader(req.Header, key, value) - } - req.Header.Set("Connection", "Upgrade") - req.Header.Set("Upgrade", "websocket") - - err = req.Write(conn) - if err != nil { - return nil, err - } - - connRF := &ConnRF{ - Conn: conn, - Req: req, - First: true, - } - - if transportConfiguration.Ed == 0 { - _, err = connRF.Read([]byte{}) - if err != nil { - return nil, err - } - } - - return connRF, nil -} - -// http.Header.Add() will convert headers to MIME header format. -// Some people don't like this because they want to send "Web*S*ocket". -// So we add a simple function to replace that method. -func AddHeader(header http.Header, key, value string) { - header[key] = append(header[key], value) -} - -func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { - errors.LogInfo(ctx, "creating connection to ", dest) - - conn, err := dialhttpUpgrade(ctx, dest, streamSettings) - if err != nil { - return nil, errors.New("failed to dial request to ", dest).Base(err) - } - return stat.Connection(conn), nil -} - -func init() { - common.Must(internet.RegisterTransportDialer(protocolName, Dial)) -} diff --git a/transport/internet/httpupgrade/httpupgrade.go b/transport/internet/httpupgrade/httpupgrade.go deleted file mode 100644 index 0a902398..00000000 --- a/transport/internet/httpupgrade/httpupgrade.go +++ /dev/null @@ -1,3 +0,0 @@ -package httpupgrade - -const protocolName = "httpupgrade" diff --git a/transport/internet/httpupgrade/httpupgrade_test.go b/transport/internet/httpupgrade/httpupgrade_test.go deleted file mode 100644 index a8108fe6..00000000 --- a/transport/internet/httpupgrade/httpupgrade_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package httpupgrade_test - -import ( - "context" - "runtime" - "testing" - "time" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol/tls/cert" - "github.com/xtls/xray-core/testing/servers/tcp" - "github.com/xtls/xray-core/transport/internet" - . "github.com/xtls/xray-core/transport/internet/httpupgrade" - "github.com/xtls/xray-core/transport/internet/stat" - "github.com/xtls/xray-core/transport/internet/tls" -) - -func Test_listenHTTPUpgradeAndDial(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenHTTPUpgrade(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ - ProtocolName: "httpupgrade", - ProtocolSettings: &Config{ - Path: "httpupgrade", - }, - }, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - - var b [1024]byte - c.SetReadDeadline(time.Now().Add(2 * time.Second)) - _, err := c.Read(b[:]) - if err != nil { - return - } - - common.Must2(c.Write([]byte("Response"))) - }(conn) - }) - common.Must(err) - - ctx := context.Background() - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "httpupgrade", - ProtocolSettings: &Config{Path: "httpupgrade"}, - } - conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - n, err := conn.Read(b[:]) - common.Must(err) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - - common.Must(conn.Close()) - conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - common.Must(err) - _, err = conn.Write([]byte("Test connection 2")) - common.Must(err) - n, err = conn.Read(b[:]) - common.Must(err) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - common.Must(conn.Close()) - - common.Must(listen.Close()) -} - -func Test_listenHTTPUpgradeAndDialWithHeaders(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenHTTPUpgrade(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ - ProtocolName: "httpupgrade", - ProtocolSettings: &Config{ - Path: "httpupgrade", - Header: map[string]string{ - "User-Agent": "Mozilla", - }, - }, - }, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - - var b [1024]byte - c.SetReadDeadline(time.Now().Add(2 * time.Second)) - _, err := c.Read(b[:]) - if err != nil { - return - } - - common.Must2(c.Write([]byte("Response"))) - }(conn) - }) - common.Must(err) - - ctx := context.Background() - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "httpupgrade", - ProtocolSettings: &Config{Path: "httpupgrade"}, - } - conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - n, err := conn.Read(b[:]) - common.Must(err) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - - common.Must(conn.Close()) - conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - common.Must(err) - _, err = conn.Write([]byte("Test connection 2")) - common.Must(err) - n, err = conn.Read(b[:]) - common.Must(err) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - common.Must(conn.Close()) - - common.Must(listen.Close()) -} - -func TestDialWithRemoteAddr(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenHTTPUpgrade(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ - ProtocolName: "httpupgrade", - ProtocolSettings: &Config{ - Path: "httpupgrade", - }, - }, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - - var b [1024]byte - _, err := c.Read(b[:]) - // common.Must(err) - if err != nil { - return - } - - _, err = c.Write([]byte(c.RemoteAddr().String())) - common.Must(err) - }(conn) - }) - common.Must(err) - - conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), &internet.MemoryStreamConfig{ - ProtocolName: "httpupgrade", - ProtocolSettings: &Config{Path: "httpupgrade", Header: map[string]string{"X-Forwarded-For": "1.1.1.1"}}, - }) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - n, err := conn.Read(b[:]) - common.Must(err) - if string(b[:n]) != "1.1.1.1:0" { - t.Error("response: ", string(b[:n])) - } - - common.Must(listen.Close()) -} - -func Test_listenHTTPUpgradeAndDial_TLS(t *testing.T) { - listenPort := tcp.PickPort() - if runtime.GOARCH == "arm64" { - return - } - - start := time.Now() - - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "httpupgrade", - ProtocolSettings: &Config{ - Path: "httpupgrades", - }, - SecurityType: "tls", - SecuritySettings: &tls.Config{ - AllowInsecure: true, - Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))}, - }, - } - listen, err := ListenHTTPUpgrade(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { - go func() { - _ = conn.Close() - }() - }) - common.Must(err) - defer listen.Close() - - conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - common.Must(err) - _ = conn.Close() - - end := time.Now() - if !end.Before(start.Add(time.Second * 5)) { - t.Error("end: ", end, " start: ", start) - } -} diff --git a/transport/internet/httpupgrade/hub.go b/transport/internet/httpupgrade/hub.go deleted file mode 100644 index dc67c747..00000000 --- a/transport/internet/httpupgrade/hub.go +++ /dev/null @@ -1,151 +0,0 @@ -package httpupgrade - -import ( - "bufio" - "context" - "crypto/tls" - "net/http" - "strings" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - http_proto "github.com/xtls/xray-core/common/protocol/http" - "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/stat" - v2tls "github.com/xtls/xray-core/transport/internet/tls" -) - -type server struct { - config *Config - addConn internet.ConnHandler - innnerListener net.Listener -} - -func (s *server) Close() error { - return s.innnerListener.Close() -} - -func (s *server) Addr() net.Addr { - return nil -} - -func (s *server) Handle(conn net.Conn) (stat.Connection, error) { - connReader := bufio.NewReader(conn) - req, err := http.ReadRequest(connReader) - if err != nil { - return nil, err - } - - if s.config != nil { - host := req.Host - if len(s.config.Host) > 0 && !internet.IsValidHTTPHost(host, s.config.Host) { - return nil, errors.New("bad host: ", host) - } - path := s.config.GetNormalizedPath() - if req.URL.Path != path { - return nil, errors.New("bad path: ", req.URL.Path) - } - } - - connection := strings.ToLower(req.Header.Get("Connection")) - upgrade := strings.ToLower(req.Header.Get("Upgrade")) - if connection != "upgrade" || upgrade != "websocket" { - _ = conn.Close() - return nil, errors.New("unrecognized request") - } - resp := &http.Response{ - Status: "101 Switching Protocols", - StatusCode: 101, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: http.Header{}, - } - resp.Header.Set("Connection", "Upgrade") - resp.Header.Set("Upgrade", "websocket") - err = resp.Write(conn) - if err != nil { - _ = conn.Close() - return nil, err - } - - forwardedAddrs := http_proto.ParseXForwardedFor(req.Header) - remoteAddr := conn.RemoteAddr() - if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { - remoteAddr = &net.TCPAddr{ - IP: forwardedAddrs[0].IP(), - Port: int(0), - } - } - - return stat.Connection(newConnection(conn, remoteAddr)), nil -} - -func (s *server) keepAccepting() { - for { - conn, err := s.innnerListener.Accept() - if err != nil { - return - } - handledConn, err := s.Handle(conn) - if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to handle request") - continue - } - s.addConn(handledConn) - } -} - -func ListenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { - transportConfiguration := streamSettings.ProtocolSettings.(*Config) - if transportConfiguration != nil { - if streamSettings.SocketSettings == nil { - streamSettings.SocketSettings = &internet.SocketConfig{} - } - streamSettings.SocketSettings.AcceptProxyProtocol = transportConfiguration.AcceptProxyProtocol || streamSettings.SocketSettings.AcceptProxyProtocol - } - var listener net.Listener - var err error - if port == net.Port(0) { // unix - listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ - Name: address.Domain(), - Net: "unix", - }, streamSettings.SocketSettings) - if err != nil { - return nil, errors.New("failed to listen unix domain socket(for HttpUpgrade) on ", address).Base(err) - } - errors.LogInfo(ctx, "listening unix domain socket(for HttpUpgrade) on ", address) - } else { // tcp - listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ - IP: address.IP(), - Port: int(port), - }, streamSettings.SocketSettings) - if err != nil { - return nil, errors.New("failed to listen TCP(for HttpUpgrade) on ", address, ":", port).Base(err) - } - errors.LogInfo(ctx, "listening TCP(for HttpUpgrade) on ", address, ":", port) - } - - if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { - errors.LogWarning(ctx, "accepting PROXY protocol") - } - - if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { - if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { - listener = tls.NewListener(listener, tlsConfig) - } - } - - serverInstance := &server{ - config: transportConfiguration, - addConn: addConn, - innnerListener: listener, - } - go serverInstance.keepAccepting() - return serverInstance, nil -} - -func init() { - common.Must(internet.RegisterTransportListener(protocolName, ListenHTTPUpgrade)) -} diff --git a/transport/internet/internet.go b/transport/internet/internet.go index 19529e63..694129ad 100644 --- a/transport/internet/internet.go +++ b/transport/internet/internet.go @@ -1,16 +1,3 @@ package internet -import ( - "net" - "strings" -) - -func IsValidHTTPHost(request string, config string) bool { - r := strings.ToLower(request) - c := strings.ToLower(config) - if strings.Contains(r, ":") { - h, _, _ := net.SplitHostPort(r) - return h == c - } - return r == c -} +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/kcp/config.go b/transport/internet/kcp/config.go index a0dfd51f..808b1d50 100644 --- a/transport/internet/kcp/config.go +++ b/transport/internet/kcp/config.go @@ -7,6 +7,8 @@ import ( "github.com/xtls/xray-core/transport/internet" ) +const protocolName = "mkcp" + // GetMTUValue returns the value of MTU settings. func (c *Config) GetMTUValue() uint32 { if c == nil || c.Mtu == nil { diff --git a/transport/internet/kcp/config.pb.go b/transport/internet/kcp/config.pb.go index 506a2dcd..0e10dac4 100644 --- a/transport/internet/kcp/config.pb.go +++ b/transport/internet/kcp/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/kcp/config.proto package kcp @@ -32,9 +32,11 @@ type MTU struct { func (x *MTU) Reset() { *x = MTU{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *MTU) String() string { @@ -45,7 +47,7 @@ func (*MTU) ProtoMessage() {} func (x *MTU) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -78,9 +80,11 @@ type TTI struct { func (x *TTI) Reset() { *x = TTI{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *TTI) String() string { @@ -91,7 +95,7 @@ func (*TTI) ProtoMessage() {} func (x *TTI) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -124,9 +128,11 @@ type UplinkCapacity struct { func (x *UplinkCapacity) Reset() { *x = UplinkCapacity{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *UplinkCapacity) String() string { @@ -137,7 +143,7 @@ func (*UplinkCapacity) ProtoMessage() {} func (x *UplinkCapacity) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -170,9 +176,11 @@ type DownlinkCapacity struct { func (x *DownlinkCapacity) Reset() { *x = DownlinkCapacity{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DownlinkCapacity) String() string { @@ -183,7 +191,7 @@ func (*DownlinkCapacity) ProtoMessage() {} func (x *DownlinkCapacity) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -216,9 +224,11 @@ type WriteBuffer struct { func (x *WriteBuffer) Reset() { *x = WriteBuffer{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *WriteBuffer) String() string { @@ -229,7 +239,7 @@ func (*WriteBuffer) ProtoMessage() {} func (x *WriteBuffer) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -262,9 +272,11 @@ type ReadBuffer struct { func (x *ReadBuffer) Reset() { *x = ReadBuffer{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ReadBuffer) String() string { @@ -275,7 +287,7 @@ func (*ReadBuffer) ProtoMessage() {} func (x *ReadBuffer) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -307,9 +319,11 @@ type ConnectionReuse struct { func (x *ConnectionReuse) Reset() { *x = ConnectionReuse{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *ConnectionReuse) String() string { @@ -320,7 +334,7 @@ func (*ConnectionReuse) ProtoMessage() {} func (x *ConnectionReuse) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -342,9 +356,7 @@ func (x *ConnectionReuse) GetEnable() bool { return false } -// Pre-shared secret between client and server. It is used for traffic obfuscation. -// Note that if seed is absent in the config, the traffic will still be obfuscated, -// but by a predefined algorithm. +// Maximum Transmission Unit, in bytes. type EncryptionSeed struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -355,9 +367,11 @@ type EncryptionSeed struct { func (x *EncryptionSeed) Reset() { *x = EncryptionSeed{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *EncryptionSeed) String() string { @@ -368,7 +382,7 @@ func (*EncryptionSeed) ProtoMessage() {} func (x *EncryptionSeed) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[7] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -408,9 +422,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_kcp_config_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_kcp_config_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -421,7 +437,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[8] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -588,7 +604,7 @@ func file_transport_internet_kcp_config_proto_rawDescGZIP() []byte { } var file_transport_internet_kcp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9) -var file_transport_internet_kcp_config_proto_goTypes = []any{ +var file_transport_internet_kcp_config_proto_goTypes = []interface{}{ (*MTU)(nil), // 0: xray.transport.internet.kcp.MTU (*TTI)(nil), // 1: xray.transport.internet.kcp.TTI (*UplinkCapacity)(nil), // 2: xray.transport.internet.kcp.UplinkCapacity @@ -621,6 +637,116 @@ func file_transport_internet_kcp_config_proto_init() { if File_transport_internet_kcp_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_kcp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MTU); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TTI); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UplinkCapacity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DownlinkCapacity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteBuffer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadBuffer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectionReuse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EncryptionSeed); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_kcp_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/kcp/config.proto b/transport/internet/kcp/config.proto index 8690f09f..a9806e6f 100644 --- a/transport/internet/kcp/config.proto +++ b/transport/internet/kcp/config.proto @@ -42,9 +42,7 @@ message ConnectionReuse { bool enable = 1; } -// Pre-shared secret between client and server. It is used for traffic obfuscation. -// Note that if seed is absent in the config, the traffic will still be obfuscated, -// but by a predefined algorithm. +// Maximum Transmission Unit, in bytes. message EncryptionSeed { string seed = 1; } diff --git a/transport/internet/kcp/connection.go b/transport/internet/kcp/connection.go index ffedcfc7..b0df75f0 100644 --- a/transport/internet/kcp/connection.go +++ b/transport/internet/kcp/connection.go @@ -2,7 +2,6 @@ package kcp import ( "bytes" - "context" "io" "net" "runtime" @@ -11,15 +10,14 @@ import ( "time" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal/semaphore" ) var ( - ErrIOTimeout = errors.New("Read/Write timeout") - ErrClosedListener = errors.New("Listener closed.") - ErrClosedConnection = errors.New("Connection closed.") + ErrIOTimeout = newError("Read/Write timeout") + ErrClosedListener = newError("Listener closed.") + ErrClosedConnection = newError("Connection closed.") ) // State of the connection @@ -205,7 +203,7 @@ type Connection struct { // NewConnection create a new KCP connection between local and remote. func NewConnection(meta ConnMetadata, writer PacketWriter, closer io.Closer, config *Config) *Connection { - errors.LogInfo(context.Background(), "#", meta.Conversation, " creating connection to ", meta.RemoteAddr) + newError("#", meta.Conversation, " creating connection to ", meta.RemoteAddr).WriteToLog() conn := &Connection{ meta: meta, @@ -430,7 +428,7 @@ func (c *Connection) SetState(state State) { current := c.Elapsed() atomic.StoreInt32((*int32)(&c.state), int32(state)) atomic.StoreUint32(&c.stateBeginTime, current) - errors.LogDebug(context.Background(), "#", c.meta.Conversation, " entering state ", state, " at ", current) + newError("#", c.meta.Conversation, " entering state ", state, " at ", current).AtDebug().WriteToLog() switch state { case StateReadyToClose: @@ -474,7 +472,7 @@ func (c *Connection) Close() error { c.SetState(StateTerminated) } - errors.LogInfo(context.Background(), "#", c.meta.Conversation, " closing connection to ", c.meta.RemoteAddr) + newError("#", c.meta.Conversation, " closing connection to ", c.meta.RemoteAddr).WriteToLog() return nil } @@ -530,7 +528,7 @@ func (c *Connection) Terminate() { if c == nil { return } - errors.LogInfo(context.Background(), "#", c.meta.Conversation, " terminating connection to ", c.RemoteAddr()) + newError("#", c.meta.Conversation, " terminating connection to ", c.RemoteAddr()).WriteToLog() // v.SetState(StateTerminated) c.dataInput.Signal() @@ -618,7 +616,7 @@ func (c *Connection) flush() { } if c.State() == StateTerminating { - errors.LogDebug(context.Background(), "#", c.meta.Conversation, " sending terminating cmd.") + newError("#", c.meta.Conversation, " sending terminating cmd.").AtDebug().WriteToLog() c.Ping(current, CommandTerminate) if current-atomic.LoadUint32(&c.stateBeginTime) > 8000 { diff --git a/transport/internet/kcp/crypt.go b/transport/internet/kcp/crypt.go index 2843379a..0a5930f0 100644 --- a/transport/internet/kcp/crypt.go +++ b/transport/internet/kcp/crypt.go @@ -6,7 +6,6 @@ import ( "hash/fnv" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" ) // SimpleAuthenticator is a legacy AEAD used for KCP encryption. @@ -65,12 +64,12 @@ func (a *SimpleAuthenticator) Open(dst, nonce, cipherText, extra []byte) ([]byte fnvHash := fnv.New32a() common.Must2(fnvHash.Write(dst[4:])) if binary.BigEndian.Uint32(dst[:4]) != fnvHash.Sum32() { - return nil, errors.New("invalid auth") + return nil, newError("invalid auth") } length := binary.BigEndian.Uint16(dst[4:6]) if len(dst)-6 != int(length) { - return nil, errors.New("invalid auth") + return nil, newError("invalid auth") } return dst[6:], nil diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index 0a3968dc..3e8d1220 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -8,7 +8,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/dice" - "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" @@ -47,22 +46,22 @@ func fetchInput(_ context.Context, input io.Reader, reader PacketReader, conn *C // DialKCP dials a new KCP connections to the specific destination. func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { dest.Network = net.Network_UDP - errors.LogInfo(ctx, "dialing mKCP to ", dest) + newError("dialing mKCP to ", dest).WriteToLog() rawConn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) if err != nil { - return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err) + return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err) } kcpSettings := streamSettings.ProtocolSettings.(*Config) header, err := kcpSettings.GetPackerHeader() if err != nil { - return nil, errors.New("failed to create packet header").Base(err) + return nil, newError("failed to create packet header").Base(err) } security, err := kcpSettings.GetSecurity() if err != nil { - return nil, errors.New("failed to create security").Base(err) + return nil, newError("failed to create security").Base(err) } reader := &KCPPacketReader{ Header: header, diff --git a/transport/internet/kcp/errors.generated.go b/transport/internet/kcp/errors.generated.go new file mode 100644 index 00000000..63c51c6f --- /dev/null +++ b/transport/internet/kcp/errors.generated.go @@ -0,0 +1,9 @@ +package kcp + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/kcp/kcp.go b/transport/internet/kcp/kcp.go index 31c0633c..1a486faa 100644 --- a/transport/internet/kcp/kcp.go +++ b/transport/internet/kcp/kcp.go @@ -6,4 +6,4 @@ // xtaci@github for translating to Golang package kcp -const protocolName = "mkcp" +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index 71ce6462..baf38e6d 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -8,7 +8,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" @@ -25,25 +24,25 @@ type ConnectionID struct { // Listener defines a server listening for connections type Listener struct { sync.Mutex - sessions map[ConnectionID]*Connection - hub *udp.Hub - tlsConfig *gotls.Config - config *Config - reader PacketReader - header internet.PacketHeader - security cipher.AEAD - addConn internet.ConnHandler + sessions map[ConnectionID]*Connection + hub *udp.Hub + tlsConfig *gotls.Config + config *Config + reader PacketReader + header internet.PacketHeader + security cipher.AEAD + addConn internet.ConnHandler } func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) { kcpSettings := streamSettings.ProtocolSettings.(*Config) header, err := kcpSettings.GetPackerHeader() if err != nil { - return nil, errors.New("failed to create packet header").Base(err).AtError() + return nil, newError("failed to create packet header").Base(err).AtError() } security, err := kcpSettings.GetSecurity() if err != nil { - return nil, errors.New("failed to create security").Base(err).AtError() + return nil, newError("failed to create security").Base(err).AtError() } l := &Listener{ header: header, @@ -68,7 +67,7 @@ func NewListener(ctx context.Context, address net.Address, port net.Port, stream l.Lock() l.hub = hub l.Unlock() - errors.LogInfo(ctx, "listening on ", address, ":", port) + newError("listening on ", address, ":", port).WriteToLog() go l.handlePackets() @@ -87,7 +86,7 @@ func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) { payload.Release() if len(segments) == 0 { - errors.LogInfo(context.Background(), "discarding invalid payload from ", src) + newError("discarding invalid payload from ", src).WriteToLog() return } diff --git a/transport/internet/memory_settings.go b/transport/internet/memory_settings.go index f1331353..3c4770d2 100644 --- a/transport/internet/memory_settings.go +++ b/transport/internet/memory_settings.go @@ -1,16 +1,12 @@ package internet -import "github.com/xtls/xray-core/common/net" - -// MemoryStreamConfig is a parsed form of StreamConfig. It is used to reduce the number of Protobuf parses. +// MemoryStreamConfig is a parsed form of StreamConfig. This is used to reduce number of Protobuf parsing. type MemoryStreamConfig struct { - Destination *net.Destination ProtocolName string ProtocolSettings interface{} SecurityType string SecuritySettings interface{} SocketSettings *SocketConfig - DownloadSettings *MemoryStreamConfig } // ToMemoryStreamConfig converts a StreamConfig to MemoryStreamConfig. It returns a default non-nil MemoryStreamConfig for nil input. @@ -26,13 +22,6 @@ func ToMemoryStreamConfig(s *StreamConfig) (*MemoryStreamConfig, error) { } if s != nil { - if s.Address != nil { - mss.Destination = &net.Destination{ - Address: s.Address.AsAddress(), - Port: net.Port(s.Port), - Network: net.Network_TCP, - } - } mss.SocketSettings = s.SocketSettings } diff --git a/transport/internet/quic/config.go b/transport/internet/quic/config.go new file mode 100644 index 00000000..fe13eec6 --- /dev/null +++ b/transport/internet/quic/config.go @@ -0,0 +1,47 @@ +package quic + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/transport/internet" + "golang.org/x/crypto/chacha20poly1305" +) + +func getAuth(config *Config) (cipher.AEAD, error) { + security := config.Security.GetSecurityType() + if security == protocol.SecurityType_NONE { + return nil, nil + } + + salted := []byte(config.Key + "xray-quic-salt") + key := sha256.Sum256(salted) + + if security == protocol.SecurityType_AES128_GCM { + block, err := aes.NewCipher(key[:16]) + common.Must(err) + return cipher.NewGCM(block) + } + + if security == protocol.SecurityType_CHACHA20_POLY1305 { + return chacha20poly1305.New(key[:]) + } + + return nil, newError("unsupported security type") +} + +func getHeader(config *Config) (internet.PacketHeader, error) { + if config.Header == nil { + return nil, nil + } + + msg, err := config.Header.GetInstance() + if err != nil { + return nil, err + } + + return internet.CreatePacketHeader(msg) +} diff --git a/transport/internet/quic/config.pb.go b/transport/internet/quic/config.pb.go new file mode 100644 index 00000000..36aa03da --- /dev/null +++ b/transport/internet/quic/config.pb.go @@ -0,0 +1,184 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: transport/internet/quic/config.proto + +package quic + +import ( + protocol "github.com/xtls/xray-core/common/protocol" + serial "github.com/xtls/xray-core/common/serial" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Security *protocol.SecurityConfig `protobuf:"bytes,2,opt,name=security,proto3" json:"security,omitempty"` + Header *serial.TypedMessage `protobuf:"bytes,3,opt,name=header,proto3" json:"header,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_quic_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_quic_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Config.ProtoReflect.Descriptor instead. +func (*Config) Descriptor() ([]byte, []int) { + return file_transport_internet_quic_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Config) GetSecurity() *protocol.SecurityConfig { + if x != nil { + return x.Security + } + return nil +} + +func (x *Config) GetHeader() *serial.TypedMessage { + if x != nil { + return x.Header + } + return nil +} + +var File_transport_internet_quic_config_proto protoreflect.FileDescriptor + +var file_transport_internet_quic_config_proto_rawDesc = []byte{ + 0x0a, 0x24, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x71, 0x75, 0x69, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, + 0x71, 0x75, 0x69, 0x63, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x96, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x40, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x63, + 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x38, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 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, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, + 0x76, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 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, 0x71, + 0x75, 0x69, 0x63, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x2f, 0x71, 0x75, 0x69, 0x63, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_transport_internet_quic_config_proto_rawDescOnce sync.Once + file_transport_internet_quic_config_proto_rawDescData = file_transport_internet_quic_config_proto_rawDesc +) + +func file_transport_internet_quic_config_proto_rawDescGZIP() []byte { + file_transport_internet_quic_config_proto_rawDescOnce.Do(func() { + file_transport_internet_quic_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_quic_config_proto_rawDescData) + }) + return file_transport_internet_quic_config_proto_rawDescData +} + +var file_transport_internet_quic_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_quic_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.transport.internet.quic.Config + (*protocol.SecurityConfig)(nil), // 1: xray.common.protocol.SecurityConfig + (*serial.TypedMessage)(nil), // 2: xray.common.serial.TypedMessage +} +var file_transport_internet_quic_config_proto_depIdxs = []int32{ + 1, // 0: xray.transport.internet.quic.Config.security:type_name -> xray.common.protocol.SecurityConfig + 2, // 1: xray.transport.internet.quic.Config.header:type_name -> xray.common.serial.TypedMessage + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_transport_internet_quic_config_proto_init() } +func file_transport_internet_quic_config_proto_init() { + if File_transport_internet_quic_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_transport_internet_quic_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_transport_internet_quic_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_quic_config_proto_goTypes, + DependencyIndexes: file_transport_internet_quic_config_proto_depIdxs, + MessageInfos: file_transport_internet_quic_config_proto_msgTypes, + }.Build() + File_transport_internet_quic_config_proto = out.File + file_transport_internet_quic_config_proto_rawDesc = nil + file_transport_internet_quic_config_proto_goTypes = nil + file_transport_internet_quic_config_proto_depIdxs = nil +} diff --git a/transport/internet/quic/config.proto b/transport/internet/quic/config.proto new file mode 100644 index 00000000..b08e531e --- /dev/null +++ b/transport/internet/quic/config.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package xray.transport.internet.quic; +option csharp_namespace = "Xray.Transport.Internet.Quic"; +option go_package = "github.com/xtls/xray-core/transport/internet/quic"; +option java_package = "com.xray.transport.internet.quic"; +option java_multiple_files = true; + +import "common/serial/typed_message.proto"; +import "common/protocol/headers.proto"; + +message Config { + string key = 1; + xray.common.protocol.SecurityConfig security = 2; + xray.common.serial.TypedMessage header = 3; +} diff --git a/transport/internet/quic/conn.go b/transport/internet/quic/conn.go new file mode 100644 index 00000000..11bee7c5 --- /dev/null +++ b/transport/internet/quic/conn.go @@ -0,0 +1,198 @@ +package quic + +import ( + "crypto/cipher" + "crypto/rand" + "errors" + "syscall" + "time" + + "github.com/quic-go/quic-go" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/transport/internet" +) + +type sysConn struct { + conn *net.UDPConn + header internet.PacketHeader + auth cipher.AEAD +} + +func wrapSysConn(rawConn *net.UDPConn, config *Config) (*sysConn, error) { + header, err := getHeader(config) + if err != nil { + return nil, err + } + auth, err := getAuth(config) + if err != nil { + return nil, err + } + return &sysConn{ + conn: rawConn, + header: header, + auth: auth, + }, nil +} + +var errInvalidPacket = errors.New("invalid packet") + +func (c *sysConn) readFromInternal(p []byte) (int, net.Addr, error) { + buffer := getBuffer() + defer putBuffer(buffer) + + nBytes, addr, err := c.conn.ReadFrom(buffer) + if err != nil { + return 0, nil, err + } + + payload := buffer[:nBytes] + if c.header != nil { + if len(payload) <= int(c.header.Size()) { + return 0, nil, errInvalidPacket + } + payload = payload[c.header.Size():] + } + + if c.auth == nil { + n := copy(p, payload) + return n, addr, nil + } + + if len(payload) <= c.auth.NonceSize() { + return 0, nil, errInvalidPacket + } + + nonce := payload[:c.auth.NonceSize()] + payload = payload[c.auth.NonceSize():] + + p, err = c.auth.Open(p[:0], nonce, payload, nil) + if err != nil { + return 0, nil, errInvalidPacket + } + + return len(p), addr, nil +} + +func (c *sysConn) ReadFrom(p []byte) (int, net.Addr, error) { + if c.header == nil && c.auth == nil { + return c.conn.ReadFrom(p) + } + + for { + n, addr, err := c.readFromInternal(p) + if err != nil && err != errInvalidPacket { + return 0, nil, err + } + if err == nil { + return n, addr, nil + } + } +} + +func (c *sysConn) WriteTo(p []byte, addr net.Addr) (int, error) { + if c.header == nil && c.auth == nil { + return c.conn.WriteTo(p, addr) + } + + buffer := getBuffer() + defer putBuffer(buffer) + + payload := buffer + n := 0 + if c.header != nil { + c.header.Serialize(payload) + n = int(c.header.Size()) + } + + if c.auth == nil { + nBytes := copy(payload[n:], p) + n += nBytes + } else { + nounce := payload[n : n+c.auth.NonceSize()] + common.Must2(rand.Read(nounce)) + n += c.auth.NonceSize() + pp := c.auth.Seal(payload[:n], nounce, p, nil) + n = len(pp) + } + + return c.conn.WriteTo(payload[:n], addr) +} + +func (c *sysConn) Close() error { + return c.conn.Close() +} + +func (c *sysConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *sysConn) SetReadBuffer(bytes int) error { + return c.conn.SetReadBuffer(bytes) +} + +func (c *sysConn) SetWriteBuffer(bytes int) error { + return c.conn.SetWriteBuffer(bytes) +} + +func (c *sysConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *sysConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *sysConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} + +func (c *sysConn) SyscallConn() (syscall.RawConn, error) { + return c.conn.SyscallConn() +} + +type interConn struct { + stream quic.Stream + local net.Addr + remote net.Addr +} + +func (c *interConn) Read(b []byte) (int, error) { + return c.stream.Read(b) +} + +func (c *interConn) WriteMultiBuffer(mb buf.MultiBuffer) error { + mb = buf.Compact(mb) + mb, err := buf.WriteMultiBuffer(c, mb) + buf.ReleaseMulti(mb) + return err +} + +func (c *interConn) Write(b []byte) (int, error) { + return c.stream.Write(b) +} + +func (c *interConn) Close() error { + return c.stream.Close() +} + +func (c *interConn) LocalAddr() net.Addr { + return c.local +} + +func (c *interConn) RemoteAddr() net.Addr { + return c.remote +} + +func (c *interConn) SetDeadline(t time.Time) error { + return c.stream.SetDeadline(t) +} + +func (c *interConn) SetReadDeadline(t time.Time) error { + return c.stream.SetReadDeadline(t) +} + +func (c *interConn) SetWriteDeadline(t time.Time) error { + return c.stream.SetWriteDeadline(t) +} diff --git a/transport/internet/quic/dialer.go b/transport/internet/quic/dialer.go new file mode 100644 index 00000000..0b9483ce --- /dev/null +++ b/transport/internet/quic/dialer.go @@ -0,0 +1,217 @@ +package quic + +import ( + "context" + "io" + "sync" + "time" + + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/task" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" +) + +type connectionContext struct { + rawConn *sysConn + conn quic.Connection +} + +var errConnectionClosed = newError("connection closed") + +func (c *connectionContext) openStream(destAddr net.Addr) (*interConn, error) { + if !isActive(c.conn) { + return nil, errConnectionClosed + } + + stream, err := c.conn.OpenStream() + if err != nil { + return nil, err + } + + conn := &interConn{ + stream: stream, + local: c.conn.LocalAddr(), + remote: destAddr, + } + + return conn, nil +} + +type clientConnections struct { + access sync.Mutex + conns map[net.Destination][]*connectionContext + cleanup *task.Periodic +} + +func isActive(s quic.Connection) bool { + select { + case <-s.Context().Done(): + return false + default: + return true + } +} + +func removeInactiveConnections(conns []*connectionContext) []*connectionContext { + activeConnections := make([]*connectionContext, 0, len(conns)) + for i, s := range conns { + if isActive(s.conn) { + activeConnections = append(activeConnections, s) + continue + } + + newError("closing quic connection at index: ", i).WriteToLog() + if err := s.conn.CloseWithError(0, ""); err != nil { + newError("failed to close connection").Base(err).WriteToLog() + } + if err := s.rawConn.Close(); err != nil { + newError("failed to close raw connection").Base(err).WriteToLog() + } + } + + if len(activeConnections) < len(conns) { + newError("active quic connection reduced from ", len(conns), " to ", len(activeConnections)).WriteToLog() + return activeConnections + } + + return conns +} + +func (s *clientConnections) cleanConnections() error { + s.access.Lock() + defer s.access.Unlock() + + if len(s.conns) == 0 { + return nil + } + + newConnMap := make(map[net.Destination][]*connectionContext) + + for dest, conns := range s.conns { + conns = removeInactiveConnections(conns) + if len(conns) > 0 { + newConnMap[dest] = conns + } + } + + s.conns = newConnMap + return nil +} + +func (s *clientConnections) openConnection(ctx context.Context, destAddr net.Addr, config *Config, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (stat.Connection, error) { + s.access.Lock() + defer s.access.Unlock() + + if s.conns == nil { + s.conns = make(map[net.Destination][]*connectionContext) + } + + dest := net.DestinationFromAddr(destAddr) + + var conns []*connectionContext + if s, found := s.conns[dest]; found { + conns = s + } + + if len(conns) > 0 { + s := conns[len(conns)-1] + if isActive(s.conn) { + conn, err := s.openStream(destAddr) + if err == nil { + return conn, nil + } + newError("failed to openStream: ").Base(err).WriteToLog() + } else { + newError("current quic connection is not active!").WriteToLog() + } + } + + conns = removeInactiveConnections(conns) + newError("dialing quic to ", dest).WriteToLog() + rawConn, err := internet.DialSystem(ctx, dest, sockopt) + if err != nil { + return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err) + } + + quicConfig := &quic.Config{ + ConnectionIDLength: 12, + KeepAlivePeriod: 0, + HandshakeIdleTimeout: time.Second * 8, + MaxIdleTimeout: time.Second * 300, + Tracer: qlog.NewTracer(func(_ logging.Perspective, connID []byte) io.WriteCloser { + return &QlogWriter{connID: connID} + }), + } + + udpConn, _ := rawConn.(*net.UDPConn) + if udpConn == nil { + udpConn = rawConn.(*internet.PacketConnWrapper).Conn.(*net.UDPConn) + } + sysConn, err := wrapSysConn(udpConn, config) + if err != nil { + rawConn.Close() + return nil, err + } + + conn, err := quic.DialContext(context.Background(), sysConn, destAddr, "", tlsConfig.GetTLSConfig(tls.WithDestination(dest)), quicConfig) + if err != nil { + sysConn.Close() + return nil, err + } + + context := &connectionContext{ + conn: conn, + rawConn: sysConn, + } + s.conns[dest] = append(conns, context) + return context.openStream(destAddr) +} + +var client clientConnections + +func init() { + client.conns = make(map[net.Destination][]*connectionContext) + client.cleanup = &task.Periodic{ + Interval: time.Minute, + Execute: client.cleanConnections, + } + common.Must(client.cleanup.Start()) +} + +func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { + tlsConfig := tls.ConfigFromStreamSettings(streamSettings) + if tlsConfig == nil { + tlsConfig = &tls.Config{ + ServerName: internalDomain, + AllowInsecure: true, + } + } + + var destAddr *net.UDPAddr + if dest.Address.Family().IsIP() { + destAddr = &net.UDPAddr{ + IP: dest.Address.IP(), + Port: int(dest.Port), + } + } else { + addr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) + if err != nil { + return nil, err + } + destAddr = addr + } + + config := streamSettings.ProtocolSettings.(*Config) + + return client.openConnection(ctx, destAddr, config, tlsConfig, streamSettings.SocketSettings) +} + +func init() { + common.Must(internet.RegisterTransportDialer(protocolName, Dial)) +} diff --git a/transport/internet/quic/errors.generated.go b/transport/internet/quic/errors.generated.go new file mode 100644 index 00000000..808fd980 --- /dev/null +++ b/transport/internet/quic/errors.generated.go @@ -0,0 +1,9 @@ +package quic + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/quic/hub.go b/transport/internet/quic/hub.go new file mode 100644 index 00000000..9b6481c5 --- /dev/null +++ b/transport/internet/quic/hub.go @@ -0,0 +1,144 @@ +package quic + +import ( + "context" + "io" + "time" + + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/protocol/tls/cert" + "github.com/xtls/xray-core/common/signal/done" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/tls" +) + +// Listener is an internet.Listener that listens for TCP connections. +type Listener struct { + rawConn *sysConn + listener quic.Listener + done *done.Instance + addConn internet.ConnHandler +} + +func (l *Listener) acceptStreams(conn quic.Connection) { + for { + stream, err := conn.AcceptStream(context.Background()) + if err != nil { + newError("failed to accept stream").Base(err).WriteToLog() + select { + case <-conn.Context().Done(): + return + case <-l.done.Wait(): + if err := conn.CloseWithError(0, ""); err != nil { + newError("failed to close connection").Base(err).WriteToLog() + } + return + default: + time.Sleep(time.Second) + continue + } + } + + conn := &interConn{ + stream: stream, + local: conn.LocalAddr(), + remote: conn.RemoteAddr(), + } + + l.addConn(conn) + } +} + +func (l *Listener) keepAccepting() { + for { + conn, err := l.listener.Accept(context.Background()) + if err != nil { + newError("failed to accept QUIC connection").Base(err).WriteToLog() + if l.done.Done() { + break + } + time.Sleep(time.Second) + continue + } + go l.acceptStreams(conn) + } +} + +// Addr implements internet.Listener.Addr. +func (l *Listener) Addr() net.Addr { + return l.listener.Addr() +} + +// Close implements internet.Listener.Close. +func (l *Listener) Close() error { + l.done.Close() + l.listener.Close() + l.rawConn.Close() + return nil +} + +// Listen creates a new Listener based on configurations. +func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { + if address.Family().IsDomain() { + return nil, newError("domain address is not allows for listening quic") + } + + tlsConfig := tls.ConfigFromStreamSettings(streamSettings) + if tlsConfig == nil { + tlsConfig = &tls.Config{ + Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.DNSNames(internalDomain), cert.CommonName(internalDomain)))}, + } + } + + config := streamSettings.ProtocolSettings.(*Config) + rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ + IP: address.IP(), + Port: int(port), + }, streamSettings.SocketSettings) + if err != nil { + return nil, err + } + + quicConfig := &quic.Config{ + ConnectionIDLength: 12, + KeepAlivePeriod: 0, + HandshakeIdleTimeout: time.Second * 8, + MaxIdleTimeout: time.Second * 300, + MaxIncomingStreams: 32, + MaxIncomingUniStreams: -1, + Tracer: qlog.NewTracer(func(_ logging.Perspective, connID []byte) io.WriteCloser { + return &QlogWriter{connID: connID} + }), + } + + conn, err := wrapSysConn(rawConn.(*net.UDPConn), config) + if err != nil { + conn.Close() + return nil, err + } + + qListener, err := quic.Listen(conn, tlsConfig.GetTLSConfig(), quicConfig) + if err != nil { + conn.Close() + return nil, err + } + + listener := &Listener{ + done: done.New(), + rawConn: conn, + listener: qListener, + addConn: handler, + } + + go listener.keepAccepting() + + return listener, nil +} + +func init() { + common.Must(internet.RegisterTransportListener(protocolName, Listen)) +} diff --git a/transport/internet/quic/pool.go b/transport/internet/quic/pool.go new file mode 100644 index 00000000..7309147f --- /dev/null +++ b/transport/internet/quic/pool.go @@ -0,0 +1,21 @@ +package quic + +import ( + "sync" + + "github.com/xtls/xray-core/common/bytespool" +) + +var pool *sync.Pool + +func init() { + pool = bytespool.GetPool(2048) +} + +func getBuffer() []byte { + return pool.Get().([]byte) +} + +func putBuffer(p []byte) { + pool.Put(p) +} diff --git a/transport/internet/quic/qlogWriter.go b/transport/internet/quic/qlogWriter.go new file mode 100644 index 00000000..1b7913e6 --- /dev/null +++ b/transport/internet/quic/qlogWriter.go @@ -0,0 +1,26 @@ +package quic + +import ( + "fmt" + + "github.com/xtls/xray-core/common/log" +) + +type QlogWriter struct { + connID []byte +} + +func (w *QlogWriter) Write(b []byte) (int, error) { + if len(b) > 1 { // skip line separator "0a" in qlog + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Debug, + Content: fmt.Sprintf("[%x] %s", w.connID, b), + }) + } + return len(b), nil +} + +func (w *QlogWriter) Close() error { + // Noop + return nil +} diff --git a/transport/internet/quic/quic.go b/transport/internet/quic/quic.go new file mode 100644 index 00000000..fe462139 --- /dev/null +++ b/transport/internet/quic/quic.go @@ -0,0 +1,25 @@ +package quic + +import ( + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/transport/internet" +) + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + +// Here is some modification needs to be done before update quic vendor. +// * use bytespool in buffer_pool.go +// * set MaxReceivePacketSize to 1452 - 32 (16 bytes auth, 16 bytes head) +// +// + +const ( + protocolName = "quic" + internalDomain = "quic.internal.example.com" +) + +func init() { + common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { + return new(Config) + })) +} diff --git a/transport/internet/quic/quic_test.go b/transport/internet/quic/quic_test.go new file mode 100644 index 00000000..ab071058 --- /dev/null +++ b/transport/internet/quic/quic_test.go @@ -0,0 +1,223 @@ +package quic_test + +import ( + "context" + "crypto/rand" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/protocol/tls/cert" + "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/testing/servers/udp" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/headers/wireguard" + "github.com/xtls/xray-core/transport/internet/quic" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" +) + +func TestQuicConnection(t *testing.T) { + port := udp.PickPort() + + listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ + ProtocolName: "quic", + ProtocolSettings: &quic.Config{}, + SecurityType: "tls", + SecuritySettings: &tls.Config{ + Certificate: []*tls.Certificate{ + tls.ParseCertificate( + cert.MustGenerate(nil, + cert.DNSNames("www.example.com"), + ), + ), + }, + }, + }, func(conn stat.Connection) { + go func() { + defer conn.Close() + + b := buf.New() + defer b.Release() + + for { + b.Clear() + if _, err := b.ReadFrom(conn); err != nil { + return + } + common.Must2(conn.Write(b.Bytes())) + } + }() + }) + common.Must(err) + + defer listener.Close() + + time.Sleep(time.Second) + + dctx := context.Background() + conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ + ProtocolName: "quic", + ProtocolSettings: &quic.Config{}, + SecurityType: "tls", + SecuritySettings: &tls.Config{ + ServerName: "www.example.com", + AllowInsecure: true, + }, + }) + common.Must(err) + defer conn.Close() + + const N = 1024 + b1 := make([]byte, N) + common.Must2(rand.Read(b1)) + b2 := buf.New() + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } +} + +func TestQuicConnectionWithoutTLS(t *testing.T) { + port := udp.PickPort() + + listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ + ProtocolName: "quic", + ProtocolSettings: &quic.Config{}, + }, func(conn stat.Connection) { + go func() { + defer conn.Close() + + b := buf.New() + defer b.Release() + + for { + b.Clear() + if _, err := b.ReadFrom(conn); err != nil { + return + } + common.Must2(conn.Write(b.Bytes())) + } + }() + }) + common.Must(err) + + defer listener.Close() + + time.Sleep(time.Second) + + dctx := context.Background() + conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ + ProtocolName: "quic", + ProtocolSettings: &quic.Config{}, + }) + common.Must(err) + defer conn.Close() + + const N = 1024 + b1 := make([]byte, N) + common.Must2(rand.Read(b1)) + b2 := buf.New() + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } +} + +func TestQuicConnectionAuthHeader(t *testing.T) { + port := udp.PickPort() + + listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ + ProtocolName: "quic", + ProtocolSettings: &quic.Config{ + Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}), + Key: "abcd", + Security: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }, + }, func(conn stat.Connection) { + go func() { + defer conn.Close() + + b := buf.New() + defer b.Release() + + for { + b.Clear() + if _, err := b.ReadFrom(conn); err != nil { + return + } + common.Must2(conn.Write(b.Bytes())) + } + }() + }) + common.Must(err) + + defer listener.Close() + + time.Sleep(time.Second) + + dctx := context.Background() + conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ + ProtocolName: "quic", + ProtocolSettings: &quic.Config{ + Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}), + Key: "abcd", + Security: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }, + }) + common.Must(err) + defer conn.Close() + + const N = 1024 + b1 := make([]byte, N) + common.Must2(rand.Read(b1)) + b2 := buf.New() + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } +} diff --git a/transport/internet/reality/config.go b/transport/internet/reality/config.go index 61674bb8..58608720 100644 --- a/transport/internet/reality/config.go +++ b/transport/internet/reality/config.go @@ -1,14 +1,10 @@ package reality import ( - "context" - "io" "net" - "os" "time" "github.com/xtls/reality" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/transport/internet" ) @@ -29,18 +25,6 @@ func (c *Config) GetREALITYConfig() *reality.Config { NextProtos: nil, // should be nil SessionTicketsDisabled: true, - - KeyLogWriter: KeyLogWriterFromConfig(c), - } - if c.LimitFallbackUpload != nil { - config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes - config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec - config.LimitFallbackUpload.BurstBytesPerSec = c.LimitFallbackUpload.BurstBytesPerSec - } - if c.LimitFallbackDownload != nil { - config.LimitFallbackDownload.AfterBytes = c.LimitFallbackDownload.AfterBytes - config.LimitFallbackDownload.BytesPerSec = c.LimitFallbackDownload.BytesPerSec - config.LimitFallbackDownload.BurstBytesPerSec = c.LimitFallbackDownload.BurstBytesPerSec } config.ServerNames = make(map[string]bool) for _, serverName := range c.ServerNames { @@ -53,19 +37,6 @@ func (c *Config) GetREALITYConfig() *reality.Config { return config } -func KeyLogWriterFromConfig(c *Config) io.Writer { - if len(c.MasterKeyLog) <= 0 || c.MasterKeyLog == "none" { - return nil - } - - writer, err := os.OpenFile(c.MasterKeyLog, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) - if err != nil { - errors.LogErrorInner(context.Background(), err, "failed to open ", c.MasterKeyLog, " as master key log") - } - - return writer -} - func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config { if settings == nil { return nil diff --git a/transport/internet/reality/config.pb.go b/transport/internet/reality/config.pb.go index c27c743b..a140d9ab 100644 --- a/transport/internet/reality/config.pb.go +++ b/transport/internet/reality/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.29.4 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/reality/config.proto package reality @@ -25,32 +25,31 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Show bool `protobuf:"varint,1,opt,name=show,proto3" json:"show,omitempty"` - Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"` - Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` - Xver uint64 `protobuf:"varint,4,opt,name=xver,proto3" json:"xver,omitempty"` - ServerNames []string `protobuf:"bytes,5,rep,name=server_names,json=serverNames,proto3" json:"server_names,omitempty"` - PrivateKey []byte `protobuf:"bytes,6,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` - MinClientVer []byte `protobuf:"bytes,7,opt,name=min_client_ver,json=minClientVer,proto3" json:"min_client_ver,omitempty"` - MaxClientVer []byte `protobuf:"bytes,8,opt,name=max_client_ver,json=maxClientVer,proto3" json:"max_client_ver,omitempty"` - MaxTimeDiff uint64 `protobuf:"varint,9,opt,name=max_time_diff,json=maxTimeDiff,proto3" json:"max_time_diff,omitempty"` - ShortIds [][]byte `protobuf:"bytes,10,rep,name=short_ids,json=shortIds,proto3" json:"short_ids,omitempty"` - Fingerprint string `protobuf:"bytes,21,opt,name=Fingerprint,proto3" json:"Fingerprint,omitempty"` - ServerName string `protobuf:"bytes,22,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` - PublicKey []byte `protobuf:"bytes,23,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - ShortId []byte `protobuf:"bytes,24,opt,name=short_id,json=shortId,proto3" json:"short_id,omitempty"` - SpiderX string `protobuf:"bytes,25,opt,name=spider_x,json=spiderX,proto3" json:"spider_x,omitempty"` - SpiderY []int64 `protobuf:"varint,26,rep,packed,name=spider_y,json=spiderY,proto3" json:"spider_y,omitempty"` - MasterKeyLog string `protobuf:"bytes,27,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"` - LimitFallbackUpload *LimitFallback `protobuf:"bytes,28,opt,name=limit_fallback_upload,json=limitFallbackUpload,proto3" json:"limit_fallback_upload,omitempty"` - LimitFallbackDownload *LimitFallback `protobuf:"bytes,29,opt,name=limit_fallback_download,json=limitFallbackDownload,proto3" json:"limit_fallback_download,omitempty"` + Show bool `protobuf:"varint,1,opt,name=show,proto3" json:"show,omitempty"` + Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + Xver uint64 `protobuf:"varint,4,opt,name=xver,proto3" json:"xver,omitempty"` + ServerNames []string `protobuf:"bytes,5,rep,name=server_names,json=serverNames,proto3" json:"server_names,omitempty"` + PrivateKey []byte `protobuf:"bytes,6,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + MinClientVer []byte `protobuf:"bytes,7,opt,name=min_client_ver,json=minClientVer,proto3" json:"min_client_ver,omitempty"` + MaxClientVer []byte `protobuf:"bytes,8,opt,name=max_client_ver,json=maxClientVer,proto3" json:"max_client_ver,omitempty"` + MaxTimeDiff uint64 `protobuf:"varint,9,opt,name=max_time_diff,json=maxTimeDiff,proto3" json:"max_time_diff,omitempty"` + ShortIds [][]byte `protobuf:"bytes,10,rep,name=short_ids,json=shortIds,proto3" json:"short_ids,omitempty"` + Fingerprint string `protobuf:"bytes,21,opt,name=Fingerprint,proto3" json:"Fingerprint,omitempty"` + ServerName string `protobuf:"bytes,22,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` + PublicKey []byte `protobuf:"bytes,23,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + ShortId []byte `protobuf:"bytes,24,opt,name=short_id,json=shortId,proto3" json:"short_id,omitempty"` + SpiderX string `protobuf:"bytes,25,opt,name=spider_x,json=spiderX,proto3" json:"spider_x,omitempty"` + SpiderY []int64 `protobuf:"varint,26,rep,packed,name=spider_y,json=spiderY,proto3" json:"spider_y,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_reality_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_reality_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -61,7 +60,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_reality_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -188,88 +187,6 @@ func (x *Config) GetSpiderY() []int64 { return nil } -func (x *Config) GetMasterKeyLog() string { - if x != nil { - return x.MasterKeyLog - } - return "" -} - -func (x *Config) GetLimitFallbackUpload() *LimitFallback { - if x != nil { - return x.LimitFallbackUpload - } - return nil -} - -func (x *Config) GetLimitFallbackDownload() *LimitFallback { - if x != nil { - return x.LimitFallbackDownload - } - return nil -} - -type LimitFallback struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - AfterBytes uint64 `protobuf:"varint,1,opt,name=after_bytes,json=afterBytes,proto3" json:"after_bytes,omitempty"` - BytesPerSec uint64 `protobuf:"varint,2,opt,name=bytes_per_sec,json=bytesPerSec,proto3" json:"bytes_per_sec,omitempty"` - BurstBytesPerSec uint64 `protobuf:"varint,3,opt,name=burst_bytes_per_sec,json=burstBytesPerSec,proto3" json:"burst_bytes_per_sec,omitempty"` -} - -func (x *LimitFallback) Reset() { - *x = LimitFallback{} - mi := &file_transport_internet_reality_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LimitFallback) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LimitFallback) ProtoMessage() {} - -func (x *LimitFallback) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_reality_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 LimitFallback.ProtoReflect.Descriptor instead. -func (*LimitFallback) Descriptor() ([]byte, []int) { - return file_transport_internet_reality_config_proto_rawDescGZIP(), []int{1} -} - -func (x *LimitFallback) GetAfterBytes() uint64 { - if x != nil { - return x.AfterBytes - } - return 0 -} - -func (x *LimitFallback) GetBytesPerSec() uint64 { - if x != nil { - return x.BytesPerSec - } - return 0 -} - -func (x *LimitFallback) GetBurstBytesPerSec() uint64 { - if x != nil { - return x.BurstBytesPerSec - } - return 0 -} - var File_transport_internet_reality_config_proto protoreflect.FileDescriptor var file_transport_internet_reality_config_proto_rawDesc = []byte{ @@ -277,7 +194,7 @@ var file_transport_internet_reality_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xce, 0x05, 0x0a, 0x06, 0x43, + 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xdc, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, @@ -307,39 +224,16 @@ var file_transport_internet_reality_config_proto_rawDesc = []byte{ 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x78, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x58, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x79, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x03, - 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x59, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x1b, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12, - 0x62, 0x0a, 0x15, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, + 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x59, 0x42, 0x7f, 0x0a, 0x23, 0x63, 0x6f, 0x6d, 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, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x13, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x12, 0x66, 0x0a, 0x17, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x1d, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x72, - 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, - 0x62, 0x61, 0x63, 0x6b, 0x52, 0x15, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x0d, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x1f, 0x0a, - 0x0b, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0a, 0x61, 0x66, 0x74, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, - 0x0a, 0x0d, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, - 0x65, 0x63, 0x12, 0x2d, 0x0a, 0x13, 0x62, 0x75, 0x72, 0x73, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x10, 0x62, 0x75, 0x72, 0x73, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, - 0x63, 0x42, 0x7f, 0x0a, 0x23, 0x63, 0x6f, 0x6d, 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, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x01, 0x5a, 0x34, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0xaa, 0x02, 0x1f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x50, 0x01, 0x5a, 0x34, 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, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0xaa, 0x02, 0x1f, 0x58, 0x72, 0x61, 0x79, 0x2e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -354,19 +248,16 @@ func file_transport_internet_reality_config_proto_rawDescGZIP() []byte { return file_transport_internet_reality_config_proto_rawDescData } -var file_transport_internet_reality_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_transport_internet_reality_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.transport.internet.reality.Config - (*LimitFallback)(nil), // 1: xray.transport.internet.reality.LimitFallback +var file_transport_internet_reality_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_reality_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.transport.internet.reality.Config } var file_transport_internet_reality_config_proto_depIdxs = []int32{ - 1, // 0: xray.transport.internet.reality.Config.limit_fallback_upload:type_name -> xray.transport.internet.reality.LimitFallback - 1, // 1: xray.transport.internet.reality.Config.limit_fallback_download:type_name -> xray.transport.internet.reality.LimitFallback - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_reality_config_proto_init() } @@ -374,13 +265,27 @@ func file_transport_internet_reality_config_proto_init() { if File_transport_internet_reality_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_reality_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_transport_internet_reality_config_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 1, NumExtensions: 0, NumServices: 0, }, diff --git a/transport/internet/reality/config.proto b/transport/internet/reality/config.proto index 83bff69f..f9ae3a4f 100644 --- a/transport/internet/reality/config.proto +++ b/transport/internet/reality/config.proto @@ -24,14 +24,4 @@ message Config { bytes short_id = 24; string spider_x = 25; repeated int64 spider_y = 26; - string master_key_log = 27; - - LimitFallback limit_fallback_upload = 28; - LimitFallback limit_fallback_download = 29; -} - -message LimitFallback { - uint64 after_bytes = 1; - uint64 bytes_per_sec = 2; - uint64 burst_bytes_per_sec = 3; } diff --git a/transport/internet/reality/errors.generated.go b/transport/internet/reality/errors.generated.go new file mode 100644 index 00000000..e578015f --- /dev/null +++ b/transport/internet/reality/errors.generated.go @@ -0,0 +1,9 @@ +package reality + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/reality/reality.go b/transport/internet/reality/reality.go index ac017ed6..835c075a 100644 --- a/transport/internet/reality/reality.go +++ b/transport/internet/reality/reality.go @@ -5,9 +5,9 @@ import ( "context" "crypto/aes" "crypto/cipher" - "crypto/ecdh" "crypto/ed25519" "crypto/hmac" + "crypto/rand" "crypto/sha256" "crypto/sha512" gotls "crypto/tls" @@ -15,6 +15,7 @@ import ( "encoding/binary" "fmt" "io" + "math/big" "net/http" "reflect" "regexp" @@ -25,7 +26,6 @@ import ( utls "github.com/refraction-networking/utls" "github.com/xtls/reality" - "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/core" @@ -34,6 +34,8 @@ import ( "golang.org/x/net/http2" ) +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + type Conn struct { *reality.Conn } @@ -104,39 +106,30 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati ServerName: config.ServerName, InsecureSkipVerify: true, SessionTicketsDisabled: true, - KeyLogWriter: KeyLogWriterFromConfig(config), } - if utlsConfig.ServerName == "" { - utlsConfig.ServerName = dest.Address.String() + if utlsConfig.ServerName == "" && dest.Address.Family().IsDomain() { + utlsConfig.ServerName = dest.Address.Domain() } uConn.ServerName = utlsConfig.ServerName fingerprint := tls.GetFingerprint(config.Fingerprint) if fingerprint == nil { - return nil, errors.New("REALITY: failed to get fingerprint").AtError() + return nil, newError("REALITY: failed to get fingerprint").AtError() } uConn.UConn = utls.UClient(c, utlsConfig, *fingerprint) { uConn.BuildHandshakeState() hello := uConn.HandshakeState.Hello hello.SessionId = make([]byte, 32) - copy(hello.Raw[39:], hello.SessionId) // the fixed location of `Session ID` + copy(hello.Raw[39:], hello.SessionId) // the location of session ID + binary.BigEndian.PutUint64(hello.SessionId, uint64(time.Now().Unix())) hello.SessionId[0] = core.Version_x hello.SessionId[1] = core.Version_y hello.SessionId[2] = core.Version_z - hello.SessionId[3] = 0 // reserved - binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix())) copy(hello.SessionId[8:], config.ShortId) if config.Show { - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16])) + fmt.Printf("REALITY localAddr: %v\thello.sessionId[:16]: %v\n", localAddr, hello.SessionId[:16]) } - publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey) - if err != nil { - return nil, errors.New("REALITY: publicKey == nil") - } - if uConn.HandshakeState.State13.KeyShareKeys.Ecdhe == nil { - return nil, errors.New("Current fingerprint ", uConn.ClientHelloID.Client, uConn.ClientHelloID.Version, " does not support TLS 1.3, REALITY handshake cannot establish.") - } - uConn.AuthKey, _ = uConn.HandshakeState.State13.KeyShareKeys.Ecdhe.ECDH(publicKey) + uConn.AuthKey = uConn.HandshakeState.State13.EcdheParams.SharedKey(config.PublicKey) if uConn.AuthKey == nil { return nil, errors.New("REALITY: SharedKey == nil") } @@ -145,24 +138,25 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati } block, _ := aes.NewCipher(uConn.AuthKey) aead, _ := cipher.NewGCM(block) - if config.Show { - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead)) - } aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw) copy(hello.Raw[39:], hello.SessionId) + if config.Show { + fmt.Printf("REALITY localAddr: %v\thello.sessionId: %v\n", localAddr, hello.SessionId) + fmt.Printf("REALITY localAddr: %v\tuConn.AuthKey: %v\n", localAddr, uConn.AuthKey) + } } - if err := uConn.HandshakeContext(ctx); err != nil { + if err := uConn.Handshake(); err != nil { return nil, err } if config.Show { - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified)) + fmt.Printf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified) } if !uConn.Verified { go func() { client := &http.Client{ Transport: &http2.Transport{ DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) { - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tDialTLSContext\n", localAddr)) + fmt.Printf("REALITY localAddr: %v\tDialTLSContext\n", localAddr) return uConn, nil }, }, @@ -170,12 +164,12 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati prefix := []byte("https://" + uConn.ServerName) maps.Lock() if maps.maps == nil { - maps.maps = make(map[string]map[string]struct{}) + maps.maps = make(map[string]map[string]bool) } paths := maps.maps[uConn.ServerName] if paths == nil { - paths = make(map[string]struct{}) - paths[config.SpiderX] = struct{}{} + paths = make(map[string]bool) + paths[config.SpiderX] = true maps.maps[uConn.ServerName] = paths } firstURL := string(prefix) + getPathLocked(paths) @@ -194,26 +188,22 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati req, _ = http.NewRequest("GET", string(prefix)+getPathLocked(paths), nil) maps.Unlock() } - if req == nil { - return - } req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map if first && config.Show { - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())) + fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent()) } times := 1 if !first { - times = int(crypto.RandBetween(config.SpiderY[4], config.SpiderY[5])) + times = int(randBetween(config.SpiderY[4], config.SpiderY[5])) } for j := 0; j < times; j++ { if !first && j == 0 { req.Header.Set("Referer", firstURL) } - req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(crypto.RandBetween(config.SpiderY[0], config.SpiderY[1])))}) + req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(randBetween(config.SpiderY[0], config.SpiderY[1])))}) if resp, err = client.Do(req); err != nil { break } - defer resp.Body.Close() req.Header.Set("Referer", req.URL.String()) if body, err = io.ReadAll(resp.Body); err != nil { break @@ -222,46 +212,44 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati for _, m := range href.FindAllSubmatch(body, -1) { m[1] = bytes.TrimPrefix(m[1], prefix) if !bytes.Contains(m[1], dot) { - paths[string(m[1])] = struct{}{} + paths[string(m[1])] = true } } req.URL.Path = getPathLocked(paths) if config.Show { - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer())) - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body))) - errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths))) + fmt.Printf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer()) + fmt.Printf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body)) + fmt.Printf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths)) } maps.Unlock() if !first { - time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval + time.Sleep(time.Duration(randBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval } } } get(true) - concurrency := int(crypto.RandBetween(config.SpiderY[2], config.SpiderY[3])) + concurrency := int(randBetween(config.SpiderY[2], config.SpiderY[3])) for i := 0; i < concurrency; i++ { go get(false) } // Do not close the connection }() - time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return - return nil, errors.New("REALITY: processed invalid connection").AtWarning() + time.Sleep(time.Duration(randBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return + return nil, errors.New("REALITY: processed invalid connection") } return uConn, nil } -var ( - href = regexp.MustCompile(`href="([/h].*?)"`) - dot = []byte(".") -) +var href = regexp.MustCompile(`href="([/h].*?)"`) +var dot = []byte(".") var maps struct { sync.Mutex - maps map[string]map[string]struct{} + maps map[string]map[string]bool } -func getPathLocked(paths map[string]struct{}) string { - stopAt := int(crypto.RandBetween(0, int64(len(paths)-1))) +func getPathLocked(paths map[string]bool) string { + stopAt := int(randBetween(0, int64(len(paths)-1))) i := 0 for s := range paths { if i == stopAt { @@ -271,3 +259,11 @@ func getPathLocked(paths map[string]struct{}) string { } return "/" } + +func randBetween(left int64, right int64) int64 { + if left == right { + return left + } + bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left)) + return left + bigInt.Int64() +} diff --git a/transport/internet/sockopt_darwin.go b/transport/internet/sockopt_darwin.go index 2c827214..5a50efa7 100644 --- a/transport/internet/sockopt_darwin.go +++ b/transport/internet/sockopt_darwin.go @@ -1,16 +1,10 @@ package internet import ( - "context" - gonet "net" "os" - "runtime" - "strconv" - "strings" "syscall" "unsafe" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "golang.org/x/sys/unix" ) @@ -41,7 +35,7 @@ const ( func OriginalDst(la, ra net.Addr) (net.IP, int, error) { f, err := os.Open("/dev/pf") if err != nil { - return net.IP{}, -1, errors.New("failed to open device /dev/pf").Base(err) + return net.IP{}, -1, newError("failed to open device /dev/pf").Base(err) } defer f.Close() fd := f.Fd() @@ -116,75 +110,20 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { if config.TcpKeepAliveIdle > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil { - return errors.New("failed to set TCP_KEEPINTVL", err) + return newError("failed to set TCP_KEEPINTVL", err) } } if config.TcpKeepAliveInterval > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil { - return errors.New("failed to set TCP_KEEPIDLE", err) + return newError("failed to set TCP_KEEPIDLE", err) } } if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) - } - } - } - - if config.Interface != "" { - iface, err := gonet.InterfaceByName(config.Interface) - - if err != nil { - return errors.New("failed to get interface ", config.Interface).Base(err) - } - if network == "tcp6" || network == "udp6" { - if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil { - return errors.New("failed to set IPV6_BOUND_IF").Base(err) - } - } else { - if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil { - return errors.New("failed to set IP_BOUND_IF").Base(err) - } - } - } - - if len(config.CustomSockopt) > 0 { - for _, custom := range config.CustomSockopt { - if custom.System != "" && custom.System != runtime.GOOS { - errors.LogDebug(context.Background(), "CustomSockopt system not match: ", "want ", custom.System, " got ", runtime.GOOS) - continue - } - // Skip unwanted network type - // network might be tcp4 or tcp6 - // use HasPrefix so that "tcp" can match tcp4/6 with "tcp" if user want to control all tcp (udp is also the same) - // if it is empty, strings.HasPrefix will always return true to make it apply for all networks - if !strings.HasPrefix(network, custom.Network) { - continue - } - var level = 0x6 // default TCP - var opt int - if len(custom.Opt) == 0 { - return errors.New("No opt!") - } else { - opt, _ = strconv.Atoi(custom.Opt) - } - if custom.Level != "" { - level, _ = strconv.Atoi(custom.Level) - } - if custom.Type == "int" { - value, _ := strconv.Atoi(custom.Value) - if err := syscall.SetsockoptInt(int(fd), level, opt, value); err != nil { - return errors.New("failed to set CustomSockoptInt", opt, value, err) - } - } else if custom.Type == "str" { - if err := syscall.SetsockoptString(int(fd), level, opt, custom.Value); err != nil { - return errors.New("failed to set CustomSockoptString", opt, custom.Value, err) - } - } else { - return errors.New("unknown CustomSockopt type:", custom.Type) + return newError("failed to unset SO_KEEPALIVE", err) } } } @@ -203,85 +142,23 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) return err } } - if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { if config.TcpKeepAliveIdle > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil { - return errors.New("failed to set TCP_KEEPINTVL", err) + return newError("failed to set TCP_KEEPINTVL", err) } } if config.TcpKeepAliveInterval > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil { - return errors.New("failed to set TCP_KEEPIDLE", err) + return newError("failed to set TCP_KEEPIDLE", err) } } if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) - } - } - } - - if config.Interface != "" { - iface, err := gonet.InterfaceByName(config.Interface) - - if err != nil { - return errors.New("failed to get interface ", config.Interface).Base(err) - } - if network == "tcp6" || network == "udp6" { - if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil { - return errors.New("failed to set IPV6_BOUND_IF").Base(err) - } - } else { - if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil { - return errors.New("failed to set IP_BOUND_IF").Base(err) - } - } - } - - if config.V6Only { - if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_V6ONLY, 1); err != nil { - return errors.New("failed to set IPV6_V6ONLY").Base(err) - } - } - - if len(config.CustomSockopt) > 0 { - for _, custom := range config.CustomSockopt { - if custom.System != "" && custom.System != runtime.GOOS { - errors.LogDebug(context.Background(), "CustomSockopt system not match: ", "want ", custom.System, " got ", runtime.GOOS) - continue - } - // Skip unwanted network type - // network might be tcp4 or tcp6 - // use HasPrefix so that "tcp" can match tcp4/6 with "tcp" if user want to control all tcp (udp is also the same) - // if it is empty, strings.HasPrefix will always return true to make it apply for all networks - if !strings.HasPrefix(network, custom.Network) { - continue - } - var level = 0x6 // default TCP - var opt int - if len(custom.Opt) == 0 { - return errors.New("No opt!") - } else { - opt, _ = strconv.Atoi(custom.Opt) - } - if custom.Level != "" { - level, _ = strconv.Atoi(custom.Level) - } - if custom.Type == "int" { - value, _ := strconv.Atoi(custom.Value) - if err := syscall.SetsockoptInt(int(fd), level, opt, value); err != nil { - return errors.New("failed to set CustomSockoptInt", opt, value, err) - } - } else if custom.Type == "str" { - if err := syscall.SetsockoptString(int(fd), level, opt, custom.Value); err != nil { - return errors.New("failed to set CustomSockoptString", opt, custom.Value, err) - } - } else { - return errors.New("unknown CustomSockopt type:", custom.Type) + return newError("failed to unset SO_KEEPALIVE", err) } } } @@ -290,41 +167,13 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } func bindAddr(fd uintptr, address []byte, port uint32) error { - setReuseAddr(fd) - setReusePort(fd) - - var sockaddr unix.Sockaddr - - switch len(address) { - case net.IPv4len: - a4 := &unix.SockaddrInet4{ - Port: int(port), - } - copy(a4.Addr[:], address) - sockaddr = a4 - case net.IPv6len: - a6 := &unix.SockaddrInet6{ - Port: int(port), - } - copy(a6.Addr[:], address) - sockaddr = a6 - default: - return errors.New("unexpected length of ip") - } - - return unix.Bind(int(fd), sockaddr) + return nil } func setReuseAddr(fd uintptr) error { - if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil { - return errors.New("failed to set SO_REUSEADDR").Base(err).AtWarning() - } return nil } func setReusePort(fd uintptr) error { - if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { - return errors.New("failed to set SO_REUSEPORT").Base(err).AtWarning() - } return nil } diff --git a/transport/internet/sockopt_freebsd.go b/transport/internet/sockopt_freebsd.go index 4d490a40..66db4be3 100644 --- a/transport/internet/sockopt_freebsd.go +++ b/transport/internet/sockopt_freebsd.go @@ -7,7 +7,6 @@ import ( "syscall" "unsafe" - "github.com/xtls/xray-core/common/errors" "golang.org/x/sys/unix" ) @@ -60,7 +59,7 @@ func (nl *pfiocNatlook) setPort(remote, local int) { func OriginalDst(la, ra net.Addr) (net.IP, int, error) { f, err := os.Open("/dev/pf") if err != nil { - return net.IP{}, -1, errors.New("failed to open device /dev/pf").Base(err) + return net.IP{}, -1, newError("failed to open device /dev/pf").Base(err) } defer f.Close() fd := f.Fd() @@ -127,7 +126,7 @@ func OriginalDst(la, ra net.Addr) (net.IP, int, error) { func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil { - return errors.New("failed to set SO_USER_COOKIE").Base(err) + return newError("failed to set SO_USER_COOKIE").Base(err) } } @@ -138,26 +137,26 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf } if tfo >= 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil { - return errors.New("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err) + return newError("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err) } } if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { - return errors.New("failed to set TCP_KEEPIDLE", err) + return newError("failed to set TCP_KEEPIDLE", err) } } if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { - return errors.New("failed to set TCP_KEEPINTVL", err) + return newError("failed to set TCP_KEEPINTVL", err) } } if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) + return newError("failed to unset SO_KEEPALIVE", err) } } } @@ -166,11 +165,11 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf ip, _, _ := net.SplitHostPort(address) if net.ParseIP(ip).To4() != nil { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil { - return errors.New("failed to set outbound IP_BINDANY").Base(err) + return newError("failed to set outbound IP_BINDANY").Base(err) } } else { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil { - return errors.New("failed to set outbound IPV6_BINDANY").Base(err) + return newError("failed to set outbound IPV6_BINDANY").Base(err) } } } @@ -180,33 +179,33 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil { - return errors.New("failed to set SO_USER_COOKIE").Base(err) + return newError("failed to set SO_USER_COOKIE").Base(err) } } if isTCPSocket(network) { tfo := config.ParseTFOValue() if tfo >= 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil { - return errors.New("failed to set TCP_FASTOPEN=", tfo).Base(err) + return newError("failed to set TCP_FASTOPEN=", tfo).Base(err) } } if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { - return errors.New("failed to set TCP_KEEPIDLE", err) + return newError("failed to set TCP_KEEPIDLE", err) } } if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { - return errors.New("failed to set TCP_KEEPINTVL", err) + return newError("failed to set TCP_KEEPINTVL", err) } } if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) + return newError("failed to unset SO_KEEPALIVE", err) } } } @@ -214,7 +213,7 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) if config.Tproxy.IsEnabled() { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil { - return errors.New("failed to set inbound IP_BINDANY").Base(err) + return newError("failed to set inbound IP_BINDANY").Base(err) } } } @@ -242,7 +241,7 @@ func bindAddr(fd uintptr, ip []byte, port uint32) error { copy(a6.Addr[:], ip) sockaddr = a6 default: - return errors.New("unexpected length of ip") + return newError("unexpected length of ip") } return syscall.Bind(int(fd), sockaddr) @@ -250,7 +249,7 @@ func bindAddr(fd uintptr, ip []byte, port uint32) error { func setReuseAddr(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { - return errors.New("failed to set SO_REUSEADDR").Base(err).AtWarning() + return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() } return nil } @@ -258,7 +257,7 @@ func setReuseAddr(fd uintptr) error { func setReusePort(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil { - return errors.New("failed to set SO_REUSEPORT").Base(err).AtWarning() + return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() } } return nil diff --git a/transport/internet/sockopt_linux.go b/transport/internet/sockopt_linux.go index be2a0e46..eda5de15 100644 --- a/transport/internet/sockopt_linux.go +++ b/transport/internet/sockopt_linux.go @@ -1,17 +1,19 @@ package internet import ( - "context" "net" - "runtime" - "strconv" - "strings" "syscall" - "github.com/xtls/xray-core/common/errors" "golang.org/x/sys/unix" ) +const ( + // For incoming connections. + TCP_FASTOPEN = 23 + // For out-going connections. + TCP_FASTOPEN_CONNECT = 30 +) + func bindAddr(fd uintptr, ip []byte, port uint32) error { setReuseAddr(fd) setReusePort(fd) @@ -32,26 +34,24 @@ func bindAddr(fd uintptr, ip []byte, port uint32) error { copy(a6.Addr[:], ip) sockaddr = a6 default: - return errors.New("unexpected length of ip") + return newError("unexpected length of ip") } return syscall.Bind(int(fd), sockaddr) } -// applyOutboundSocketOptions applies socket options for outbound connection. -// note that unlike other part of Xray, this function needs network with speified network stack(tcp4/tcp6/udp4/udp6) func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(config.Mark)); err != nil { - return errors.New("failed to set SO_MARK").Base(err) + return newError("failed to set SO_MARK").Base(err) } } - if config.Interface != "" { - if err := syscall.BindToDevice(int(fd), config.Interface); err != nil { - return errors.New("failed to set Interface").Base(err) - } - } + if config.Interface != "" { + if err := syscall.BindToDevice(int(fd), config.Interface); err != nil { + return newError("failed to set Interface").Base(err) + } + } if isTCPSocket(network) { tfo := config.ParseTFOValue() @@ -59,205 +59,103 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf tfo = 1 } if tfo >= 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, unix.TCP_FASTOPEN_CONNECT, tfo); err != nil { - return errors.New("failed to set TCP_FASTOPEN_CONNECT", tfo).Base(err) + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN_CONNECT, tfo); err != nil { + return newError("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err) } } if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { - return errors.New("failed to set TCP_KEEPINTVL", err) + return newError("failed to set TCP_KEEPINTVL", err) } } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { - return errors.New("failed to set TCP_KEEPIDLE", err) + return newError("failed to set TCP_KEEPIDLE", err) } } if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) + return newError("failed to unset SO_KEEPALIVE", err) } } if config.TcpCongestion != "" { if err := syscall.SetsockoptString(int(fd), syscall.SOL_TCP, syscall.TCP_CONGESTION, config.TcpCongestion); err != nil { - return errors.New("failed to set TCP_CONGESTION", err) + return newError("failed to set TCP_CONGESTION", err) } } if config.TcpWindowClamp > 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_WINDOW_CLAMP, int(config.TcpWindowClamp)); err != nil { - return errors.New("failed to set TCP_WINDOW_CLAMP", err) - } - } - - if config.TcpUserTimeout > 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int(config.TcpUserTimeout)); err != nil { - return errors.New("failed to set TCP_USER_TIMEOUT", err) - } - } - - if config.TcpMaxSeg > 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_MAXSEG, int(config.TcpMaxSeg)); err != nil { - return errors.New("failed to set TCP_MAXSEG", err) - } - } - - } - - if len(config.CustomSockopt) > 0 { - for _, custom := range config.CustomSockopt { - if custom.System != "" && custom.System != runtime.GOOS { - errors.LogDebug(context.Background(), "CustomSockopt system not match: ", "want ", custom.System, " got ", runtime.GOOS) - continue - } - // Skip unwanted network type - // network might be tcp4 or tcp6 - // use HasPrefix so that "tcp" can match tcp4/6 with "tcp" if user want to control all tcp (udp is also the same) - // if it is empty, strings.HasPrefix will always return true to make it apply for all networks - if !strings.HasPrefix(network, custom.Network) { - continue - } - var level = 0x6 // default TCP - var opt int - if len(custom.Opt) == 0 { - return errors.New("No opt!") - } else { - opt, _ = strconv.Atoi(custom.Opt) - } - if custom.Level != "" { - level, _ = strconv.Atoi(custom.Level) - } - if custom.Type == "int" { - value, _ := strconv.Atoi(custom.Value) - if err := syscall.SetsockoptInt(int(fd), level, opt, value); err != nil { - return errors.New("failed to set CustomSockoptInt", opt, value, err) - } - } else if custom.Type == "str" { - if err := syscall.SetsockoptString(int(fd), level, opt, custom.Value); err != nil { - return errors.New("failed to set CustomSockoptString", opt, custom.Value, err) - } - } else { - return errors.New("unknown CustomSockopt type:", custom.Type) - } - } + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_WINDOW_CLAMP, int(config.TcpWindowClamp)); err != nil { + return newError("failed to set TCP_WINDOW_CLAMP", err) + } + } } if config.Tproxy.IsEnabled() { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { - return errors.New("failed to set IP_TRANSPARENT").Base(err) + return newError("failed to set IP_TRANSPARENT").Base(err) } } return nil } -// applyInboundSocketOptions applies socket options for inbound listener. -// note that unlike other part of Xray, this function needs network with speified network stack(tcp4/tcp6/udp4/udp6) func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(config.Mark)); err != nil { - return errors.New("failed to set SO_MARK").Base(err) + return newError("failed to set SO_MARK").Base(err) } } if isTCPSocket(network) { tfo := config.ParseTFOValue() if tfo >= 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, unix.TCP_FASTOPEN, tfo); err != nil { - return errors.New("failed to set TCP_FASTOPEN", tfo).Base(err) + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN, tfo); err != nil { + return newError("failed to set TCP_FASTOPEN=", tfo).Base(err) } } if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { - return errors.New("failed to set TCP_KEEPINTVL", err) + return newError("failed to set TCP_KEEPINTVL", err) } } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { - return errors.New("failed to set TCP_KEEPIDLE", err) + return newError("failed to set TCP_KEEPIDLE", err) } } if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) + return newError("failed to unset SO_KEEPALIVE", err) } } if config.TcpCongestion != "" { if err := syscall.SetsockoptString(int(fd), syscall.SOL_TCP, syscall.TCP_CONGESTION, config.TcpCongestion); err != nil { - return errors.New("failed to set TCP_CONGESTION", err) + return newError("failed to set TCP_CONGESTION", err) } } if config.TcpWindowClamp > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_WINDOW_CLAMP, int(config.TcpWindowClamp)); err != nil { - return errors.New("failed to set TCP_WINDOW_CLAMP", err) - } - } - - if config.TcpUserTimeout > 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int(config.TcpUserTimeout)); err != nil { - return errors.New("failed to set TCP_USER_TIMEOUT", err) - } - } - - if config.TcpMaxSeg > 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_MAXSEG, int(config.TcpMaxSeg)); err != nil { - return errors.New("failed to set TCP_MAXSEG", err) - } - } - if len(config.CustomSockopt) > 0 { - for _, custom := range config.CustomSockopt { - if custom.System != "" && custom.System != runtime.GOOS { - errors.LogDebug(context.Background(), "CustomSockopt system not match: ", "want ", custom.System, " got ", runtime.GOOS) - continue - } - // Skip unwanted network type - // network might be tcp4 or tcp6 - // use HasPrefix so that "tcp" can match tcp4/6 with "tcp" if user want to control all tcp (udp is also the same) - // if it is empty, strings.HasPrefix will always return true to make it apply for all networks - if !strings.HasPrefix(network, custom.Network) { - continue - } - var level = 0x6 // default TCP - var opt int - if len(custom.Opt) == 0 { - return errors.New("No opt!") - } else { - opt, _ = strconv.Atoi(custom.Opt) - } - if custom.Level != "" { - level, _ = strconv.Atoi(custom.Level) - } - if custom.Type == "int" { - value, _ := strconv.Atoi(custom.Value) - if err := syscall.SetsockoptInt(int(fd), level, opt, value); err != nil { - return errors.New("failed to set CustomSockoptInt", opt, value, err) - } - } else if custom.Type == "str" { - if err := syscall.SetsockoptString(int(fd), level, opt, custom.Value); err != nil { - return errors.New("failed to set CustomSockoptString", opt, custom.Value, err) - } - } else { - return errors.New("unknown CustomSockopt type:", custom.Type) - } - } + return newError("failed to set TCP_WINDOW_CLAMP", err) + } } } if config.Tproxy.IsEnabled() { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { - return errors.New("failed to set IP_TRANSPARENT").Base(err) + return newError("failed to set IP_TRANSPARENT").Base(err) } } @@ -271,7 +169,7 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) if config.V6Only { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, syscall.IPV6_V6ONLY, 1); err != nil { - return errors.New("failed to set IPV6_V6ONLY", err) + return newError("failed to set IPV6_V6ONLY", err) } } @@ -280,14 +178,14 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) func setReuseAddr(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { - return errors.New("failed to set SO_REUSEADDR").Base(err).AtWarning() + return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() } return nil } func setReusePort(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { - return errors.New("failed to set SO_REUSEPORT").Base(err).AtWarning() + return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() } return nil } diff --git a/transport/internet/sockopt_other.go b/transport/internet/sockopt_other.go index 7e91110e..ebcf4e26 100644 --- a/transport/internet/sockopt_other.go +++ b/transport/internet/sockopt_other.go @@ -1,5 +1,5 @@ -//go:build js || netbsd || openbsd || solaris -// +build js netbsd openbsd solaris +//go:build js || dragonfly || netbsd || openbsd || solaris +// +build js dragonfly netbsd openbsd solaris package internet diff --git a/transport/internet/sockopt_windows.go b/transport/internet/sockopt_windows.go index 1389ca06..ccc7b039 100644 --- a/transport/internet/sockopt_windows.go +++ b/transport/internet/sockopt_windows.go @@ -1,22 +1,11 @@ package internet import ( - "context" - "encoding/binary" - "net" - "runtime" - "strconv" - "strings" "syscall" - "unsafe" - - "github.com/xtls/xray-core/common/errors" ) const ( - TCP_FASTOPEN = 15 - IP_UNICAST_IF = 31 - IPV6_UNICAST_IF = 31 + TCP_FASTOPEN = 15 ) func setTFO(fd syscall.Handle, tfo int) error { @@ -32,87 +21,17 @@ func setTFO(fd syscall.Handle, tfo int) error { } func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { - if config.Interface != "" { - inf, err := net.InterfaceByName(config.Interface) - if err != nil { - return errors.New("failed to find the interface").Base(err) - } - // easy way to check if the address is ipv4 - isV4 := strings.Contains(address, ".") - // note: DO NOT trust the passed network variable, it can be udp6 even if the address is ipv4 - // because operating system might(always) use ipv6 socket to process ipv4 - host, _, err := net.SplitHostPort(address) - if isV4 { - var bytes [4]byte - binary.BigEndian.PutUint32(bytes[:], uint32(inf.Index)) - idx := *(*uint32)(unsafe.Pointer(&bytes[0])) - if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx)); err != nil { - return errors.New("failed to set IP_UNICAST_IF").Base(err) - } - if ip := net.ParseIP(host); ip != nil && ip.IsMulticast() && isUDPSocket(network) { - if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, int(idx)); err != nil { - return errors.New("failed to set IP_MULTICAST_IF").Base(err) - } - } - } else { - if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, inf.Index); err != nil { - return errors.New("failed to set IPV6_UNICAST_IF").Base(err) - } - if ip := net.ParseIP(host); ip != nil && ip.IsMulticast() && isUDPSocket(network) { - if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, inf.Index); err != nil { - return errors.New("failed to set IPV6_MULTICAST_IF").Base(err) - } - } - } - } - if isTCPSocket(network) { if err := setTFO(syscall.Handle(fd), config.ParseTFOValue()); err != nil { return err } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveIdle < 0 { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) - } - } - } - - if len(config.CustomSockopt) > 0 { - for _, custom := range config.CustomSockopt { - if custom.System != "" && custom.System != runtime.GOOS { - errors.LogDebug(context.Background(), "CustomSockopt system not match: ", "want ", custom.System, " got ", runtime.GOOS) - continue - } - // Skip unwanted network type - // network might be tcp4 or tcp6 - // use HasPrefix so that "tcp" can match tcp4/6 with "tcp" if user want to control all tcp (udp is also the same) - // if it is empty, strings.HasPrefix will always return true to make it apply for all networks - if !strings.HasPrefix(network, custom.Network) { - continue - } - var level = 0x6 // default TCP - var opt int - if len(custom.Opt) == 0 { - return errors.New("No opt!") - } else { - opt, _ = strconv.Atoi(custom.Opt) - } - if custom.Level != "" { - level, _ = strconv.Atoi(custom.Level) - } - if custom.Type == "int" { - value, _ := strconv.Atoi(custom.Value) - if err := syscall.SetsockoptInt(syscall.Handle(fd), level, opt, value); err != nil { - return errors.New("failed to set CustomSockoptInt", opt, value, err) - } - } else if custom.Type == "str" { - return errors.New("failed to set CustomSockoptString: Str type does not supported on windows") - } else { - return errors.New("unknown CustomSockopt type:", custom.Type) + return newError("failed to unset SO_KEEPALIVE", err) } } } @@ -127,53 +46,11 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { - return errors.New("failed to set SO_KEEPALIVE", err) + return newError("failed to set SO_KEEPALIVE", err) } } else if config.TcpKeepAliveIdle < 0 { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { - return errors.New("failed to unset SO_KEEPALIVE", err) - } - } - } - - if config.V6Only { - if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1); err != nil { - return errors.New("failed to set IPV6_V6ONLY").Base(err) - } - } - - if len(config.CustomSockopt) > 0 { - for _, custom := range config.CustomSockopt { - if custom.System != "" && custom.System != runtime.GOOS { - errors.LogDebug(context.Background(), "CustomSockopt system not match: ", "want ", custom.System, " got ", runtime.GOOS) - continue - } - // Skip unwanted network type - // network might be tcp4 or tcp6 - // use HasPrefix so that "tcp" can match tcp4/6 with "tcp" if user want to control all tcp (udp is also the same) - // if it is empty, strings.HasPrefix will always return true to make it apply for all networks - if !strings.HasPrefix(network, custom.Network) { - continue - } - var level = 0x6 // default TCP - var opt int - if len(custom.Opt) == 0 { - return errors.New("No opt!") - } else { - opt, _ = strconv.Atoi(custom.Opt) - } - if custom.Level != "" { - level, _ = strconv.Atoi(custom.Level) - } - if custom.Type == "int" { - value, _ := strconv.Atoi(custom.Value) - if err := syscall.SetsockoptInt(syscall.Handle(fd), level, opt, value); err != nil { - return errors.New("failed to set CustomSockoptInt", opt, value, err) - } - } else if custom.Type == "str" { - return errors.New("failed to set CustomSockoptString: Str type does not supported on windows") - } else { - return errors.New("unknown CustomSockopt type:", custom.Type) + return newError("failed to unset SO_KEEPALIVE", err) } } } diff --git a/transport/internet/splithttp/browser_client.go b/transport/internet/splithttp/browser_client.go deleted file mode 100644 index e4317aa2..00000000 --- a/transport/internet/splithttp/browser_client.go +++ /dev/null @@ -1,48 +0,0 @@ -package splithttp - -import ( - "context" - "io" - gonet "net" - - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/browser_dialer" - "github.com/xtls/xray-core/transport/internet/websocket" -) - -// BrowserDialerClient implements splithttp.DialerClient in terms of browser dialer -type BrowserDialerClient struct { - transportConfig *Config -} - -func (c *BrowserDialerClient) IsClosed() bool { - panic("not implemented yet") -} - -func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (io.ReadCloser, gonet.Addr, gonet.Addr, error) { - if body != nil { - return nil, nil, nil, errors.New("bidirectional streaming for browser dialer not implemented yet") - } - - conn, err := browser_dialer.DialGet(url, c.transportConfig.GetRequestHeader(url)) - dummyAddr := &gonet.IPAddr{} - if err != nil { - return nil, dummyAddr, dummyAddr, err - } - - return websocket.NewConnection(conn, dummyAddr, nil, 0), conn.RemoteAddr(), conn.LocalAddr(), nil -} - -func (c *BrowserDialerClient) PostPacket(ctx context.Context, url string, body io.Reader, contentLength int64) error { - bytes, err := io.ReadAll(body) - if err != nil { - return err - } - - err = browser_dialer.DialPost(url, c.transportConfig.GetRequestHeader(url), bytes) - if err != nil { - return err - } - - return nil -} diff --git a/transport/internet/splithttp/client.go b/transport/internet/splithttp/client.go deleted file mode 100644 index 0f809838..00000000 --- a/transport/internet/splithttp/client.go +++ /dev/null @@ -1,210 +0,0 @@ -package splithttp - -import ( - "bytes" - "context" - "fmt" - "io" - gonet "net" - "net/http" - "net/http/httptrace" - "sync" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/signal/done" -) - -// interface to abstract between use of browser dialer, vs net/http -type DialerClient interface { - IsClosed() bool - - // ctx, url, body, uploadOnly - OpenStream(context.Context, string, io.Reader, bool) (io.ReadCloser, net.Addr, net.Addr, error) - - // ctx, url, body, contentLength - PostPacket(context.Context, string, io.Reader, int64) error -} - -// implements splithttp.DialerClient in terms of direct network connections -type DefaultDialerClient struct { - transportConfig *Config - client *http.Client - closed bool - httpVersion string - // pool of net.Conn, created using dialUploadConn - uploadRawPool *sync.Pool - dialUploadConn func(ctxInner context.Context) (net.Conn, error) -} - -func (c *DefaultDialerClient) IsClosed() bool { - return c.closed -} - -func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (wrc io.ReadCloser, remoteAddr, localAddr gonet.Addr, err error) { - // this is done when the TCP/UDP connection to the server was established, - // and we can unblock the Dial function and print correct net addresses in - // logs - gotConn := done.New() - ctx = httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{ - GotConn: func(connInfo httptrace.GotConnInfo) { - remoteAddr = connInfo.Conn.RemoteAddr() - localAddr = connInfo.Conn.LocalAddr() - gotConn.Close() - }, - }) - - method := "GET" // stream-down - if body != nil { - method = "POST" // stream-up/one - } - req, _ := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body) - req.Header = c.transportConfig.GetRequestHeader(url) - if method == "POST" && !c.transportConfig.NoGRPCHeader { - req.Header.Set("Content-Type", "application/grpc") - } - - wrc = &WaitReadCloser{Wait: make(chan struct{})} - go func() { - resp, err := c.client.Do(req) - if err != nil { - if !uploadOnly { // stream-down is enough - c.closed = true - errors.LogInfoInner(ctx, err, "failed to "+method+" "+url) - } - gotConn.Close() - wrc.Close() - return - } - if resp.StatusCode != 200 && !uploadOnly { - errors.LogInfo(ctx, "unexpected status ", resp.StatusCode) - } - if resp.StatusCode != 200 || uploadOnly { // stream-up - io.Copy(io.Discard, resp.Body) - resp.Body.Close() // if it is called immediately, the upload will be interrupted also - wrc.Close() - return - } - wrc.(*WaitReadCloser).Set(resp.Body) - }() - - <-gotConn.Wait() - return -} - -func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body io.Reader, contentLength int64) error { - req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), "POST", url, body) - if err != nil { - return err - } - req.ContentLength = contentLength - req.Header = c.transportConfig.GetRequestHeader(url) - - if c.httpVersion != "1.1" { - resp, err := c.client.Do(req) - if err != nil { - c.closed = true - return err - } - - io.Copy(io.Discard, resp.Body) - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return errors.New("bad status code:", resp.Status) - } - } else { - // stringify the entire HTTP/1.1 request so it can be - // safely retried. if instead req.Write is called multiple - // times, the body is already drained after the first - // request - requestBuff := new(bytes.Buffer) - common.Must(req.Write(requestBuff)) - - var uploadConn any - var h1UploadConn *H1Conn - - for { - uploadConn = c.uploadRawPool.Get() - newConnection := uploadConn == nil - if newConnection { - newConn, err := c.dialUploadConn(context.WithoutCancel(ctx)) - if err != nil { - return err - } - h1UploadConn = NewH1Conn(newConn) - uploadConn = h1UploadConn - } else { - h1UploadConn = uploadConn.(*H1Conn) - - // TODO: Replace 0 here with a config value later - // Or add some other condition for optimization purposes - if h1UploadConn.UnreadedResponsesCount > 0 { - resp, err := http.ReadResponse(h1UploadConn.RespBufReader, req) - if err != nil { - c.closed = true - return fmt.Errorf("error while reading response: %s", err.Error()) - } - io.Copy(io.Discard, resp.Body) - defer resp.Body.Close() - if resp.StatusCode != 200 { - return fmt.Errorf("got non-200 error response code: %d", resp.StatusCode) - } - } - } - - _, err := h1UploadConn.Write(requestBuff.Bytes()) - // if the write failed, we try another connection from - // the pool, until the write on a new connection fails. - // failed writes to a pooled connection are normal when - // the connection has been closed in the meantime. - if err == nil { - break - } else if newConnection { - return err - } - } - - c.uploadRawPool.Put(uploadConn) - } - - return nil -} - -type WaitReadCloser struct { - Wait chan struct{} - io.ReadCloser -} - -func (w *WaitReadCloser) Set(rc io.ReadCloser) { - w.ReadCloser = rc - defer func() { - if recover() != nil { - rc.Close() - } - }() - close(w.Wait) -} - -func (w *WaitReadCloser) Read(b []byte) (int, error) { - if w.ReadCloser == nil { - if <-w.Wait; w.ReadCloser == nil { - return 0, io.ErrClosedPipe - } - } - return w.ReadCloser.Read(b) -} - -func (w *WaitReadCloser) Close() error { - if w.ReadCloser != nil { - return w.ReadCloser.Close() - } - defer func() { - if recover() != nil && w.ReadCloser != nil { - w.ReadCloser.Close() - } - }() - close(w.Wait) - return nil -} diff --git a/transport/internet/splithttp/config.go b/transport/internet/splithttp/config.go deleted file mode 100644 index 21e81aa1..00000000 --- a/transport/internet/splithttp/config.go +++ /dev/null @@ -1,187 +0,0 @@ -package splithttp - -import ( - "net/http" - "net/url" - "strings" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/crypto" - "github.com/xtls/xray-core/transport/internet" -) - -func (c *Config) GetNormalizedPath() string { - pathAndQuery := strings.SplitN(c.Path, "?", 2) - path := pathAndQuery[0] - - if path == "" || path[0] != '/' { - path = "/" + path - } - - if path[len(path)-1] != '/' { - path = path + "/" - } - - return path -} - -func (c *Config) GetNormalizedQuery() string { - pathAndQuery := strings.SplitN(c.Path, "?", 2) - query := "" - - if len(pathAndQuery) > 1 { - query = pathAndQuery[1] - } - - /* - if query != "" { - query += "&" - } - query += "x_version=" + core.Version() - */ - - return query -} - -func (c *Config) GetRequestHeader(rawURL string) http.Header { - header := http.Header{} - for k, v := range c.Headers { - header.Add(k, v) - } - - u, _ := url.Parse(rawURL) - // https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B - // h2's HPACK Header Compression feature employs a huffman encoding using a static table. - // 'X' is assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire. - // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2 - // h3's similar QPACK feature uses the same huffman table. - u.RawQuery = "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand())) - header.Set("Referer", u.String()) - - return header -} - -func (c *Config) WriteResponseHeader(writer http.ResponseWriter) { - // CORS headers for the browser dialer - writer.Header().Set("Access-Control-Allow-Origin", "*") - writer.Header().Set("Access-Control-Allow-Methods", "GET, POST") - // writer.Header().Set("X-Version", core.Version()) - writer.Header().Set("X-Padding", strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand()))) -} - -func (c *Config) GetNormalizedXPaddingBytes() RangeConfig { - if c.XPaddingBytes == nil || c.XPaddingBytes.To == 0 { - return RangeConfig{ - From: 100, - To: 1000, - } - } - - return *c.XPaddingBytes -} - -func (c *Config) GetNormalizedScMaxEachPostBytes() RangeConfig { - if c.ScMaxEachPostBytes == nil || c.ScMaxEachPostBytes.To == 0 { - return RangeConfig{ - From: 1000000, - To: 1000000, - } - } - - return *c.ScMaxEachPostBytes -} - -func (c *Config) GetNormalizedScMinPostsIntervalMs() RangeConfig { - if c.ScMinPostsIntervalMs == nil || c.ScMinPostsIntervalMs.To == 0 { - return RangeConfig{ - From: 30, - To: 30, - } - } - - return *c.ScMinPostsIntervalMs -} - -func (c *Config) GetNormalizedScMaxBufferedPosts() int { - if c.ScMaxBufferedPosts == 0 { - return 30 - } - - return int(c.ScMaxBufferedPosts) -} - -func (c *Config) GetNormalizedScStreamUpServerSecs() RangeConfig { - if c.ScStreamUpServerSecs == nil || c.ScStreamUpServerSecs.To == 0 { - return RangeConfig{ - From: 20, - To: 80, - } - } - - return *c.ScMinPostsIntervalMs -} - -func (m *XmuxConfig) GetNormalizedMaxConcurrency() RangeConfig { - if m.MaxConcurrency == nil { - return RangeConfig{ - From: 0, - To: 0, - } - } - - return *m.MaxConcurrency -} - -func (m *XmuxConfig) GetNormalizedMaxConnections() RangeConfig { - if m.MaxConnections == nil { - return RangeConfig{ - From: 0, - To: 0, - } - } - - return *m.MaxConnections -} - -func (m *XmuxConfig) GetNormalizedCMaxReuseTimes() RangeConfig { - if m.CMaxReuseTimes == nil { - return RangeConfig{ - From: 0, - To: 0, - } - } - - return *m.CMaxReuseTimes -} - -func (m *XmuxConfig) GetNormalizedHMaxRequestTimes() RangeConfig { - if m.HMaxRequestTimes == nil { - return RangeConfig{ - From: 0, - To: 0, - } - } - - return *m.HMaxRequestTimes -} - -func (m *XmuxConfig) GetNormalizedHMaxReusableSecs() RangeConfig { - if m.HMaxReusableSecs == nil { - return RangeConfig{ - From: 0, - To: 0, - } - } - - return *m.HMaxReusableSecs -} - -func init() { - common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { - return new(Config) - })) -} - -func (c RangeConfig) rand() int32 { - return int32(crypto.RandBetween(int64(c.From), int64(c.To))) -} diff --git a/transport/internet/splithttp/config.pb.go b/transport/internet/splithttp/config.pb.go deleted file mode 100644 index 98377722..00000000 --- a/transport/internet/splithttp/config.pb.go +++ /dev/null @@ -1,476 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/splithttp/config.proto - -package splithttp - -import ( - internet "github.com/xtls/xray-core/transport/internet" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type RangeConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - From int32 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"` - To int32 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"` -} - -func (x *RangeConfig) Reset() { - *x = RangeConfig{} - mi := &file_transport_internet_splithttp_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RangeConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RangeConfig) ProtoMessage() {} - -func (x *RangeConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_splithttp_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 RangeConfig.ProtoReflect.Descriptor instead. -func (*RangeConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_splithttp_config_proto_rawDescGZIP(), []int{0} -} - -func (x *RangeConfig) GetFrom() int32 { - if x != nil { - return x.From - } - return 0 -} - -func (x *RangeConfig) GetTo() int32 { - if x != nil { - return x.To - } - return 0 -} - -type XmuxConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - MaxConcurrency *RangeConfig `protobuf:"bytes,1,opt,name=maxConcurrency,proto3" json:"maxConcurrency,omitempty"` - MaxConnections *RangeConfig `protobuf:"bytes,2,opt,name=maxConnections,proto3" json:"maxConnections,omitempty"` - CMaxReuseTimes *RangeConfig `protobuf:"bytes,3,opt,name=cMaxReuseTimes,proto3" json:"cMaxReuseTimes,omitempty"` - HMaxRequestTimes *RangeConfig `protobuf:"bytes,4,opt,name=hMaxRequestTimes,proto3" json:"hMaxRequestTimes,omitempty"` - HMaxReusableSecs *RangeConfig `protobuf:"bytes,5,opt,name=hMaxReusableSecs,proto3" json:"hMaxReusableSecs,omitempty"` - HKeepAlivePeriod int64 `protobuf:"varint,6,opt,name=hKeepAlivePeriod,proto3" json:"hKeepAlivePeriod,omitempty"` -} - -func (x *XmuxConfig) Reset() { - *x = XmuxConfig{} - mi := &file_transport_internet_splithttp_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *XmuxConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*XmuxConfig) ProtoMessage() {} - -func (x *XmuxConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_splithttp_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 XmuxConfig.ProtoReflect.Descriptor instead. -func (*XmuxConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_splithttp_config_proto_rawDescGZIP(), []int{1} -} - -func (x *XmuxConfig) GetMaxConcurrency() *RangeConfig { - if x != nil { - return x.MaxConcurrency - } - return nil -} - -func (x *XmuxConfig) GetMaxConnections() *RangeConfig { - if x != nil { - return x.MaxConnections - } - return nil -} - -func (x *XmuxConfig) GetCMaxReuseTimes() *RangeConfig { - if x != nil { - return x.CMaxReuseTimes - } - return nil -} - -func (x *XmuxConfig) GetHMaxRequestTimes() *RangeConfig { - if x != nil { - return x.HMaxRequestTimes - } - return nil -} - -func (x *XmuxConfig) GetHMaxReusableSecs() *RangeConfig { - if x != nil { - return x.HMaxReusableSecs - } - return nil -} - -func (x *XmuxConfig) GetHKeepAlivePeriod() int64 { - if x != nil { - return x.HKeepAlivePeriod - } - return 0 -} - -type Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - Mode string `protobuf:"bytes,3,opt,name=mode,proto3" json:"mode,omitempty"` - Headers map[string]string `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XPaddingBytes *RangeConfig `protobuf:"bytes,5,opt,name=xPaddingBytes,proto3" json:"xPaddingBytes,omitempty"` - NoGRPCHeader bool `protobuf:"varint,6,opt,name=noGRPCHeader,proto3" json:"noGRPCHeader,omitempty"` - NoSSEHeader bool `protobuf:"varint,7,opt,name=noSSEHeader,proto3" json:"noSSEHeader,omitempty"` - ScMaxEachPostBytes *RangeConfig `protobuf:"bytes,8,opt,name=scMaxEachPostBytes,proto3" json:"scMaxEachPostBytes,omitempty"` - ScMinPostsIntervalMs *RangeConfig `protobuf:"bytes,9,opt,name=scMinPostsIntervalMs,proto3" json:"scMinPostsIntervalMs,omitempty"` - ScMaxBufferedPosts int64 `protobuf:"varint,10,opt,name=scMaxBufferedPosts,proto3" json:"scMaxBufferedPosts,omitempty"` - ScStreamUpServerSecs *RangeConfig `protobuf:"bytes,11,opt,name=scStreamUpServerSecs,proto3" json:"scStreamUpServerSecs,omitempty"` - Xmux *XmuxConfig `protobuf:"bytes,12,opt,name=xmux,proto3" json:"xmux,omitempty"` - DownloadSettings *internet.StreamConfig `protobuf:"bytes,13,opt,name=downloadSettings,proto3" json:"downloadSettings,omitempty"` -} - -func (x *Config) Reset() { - *x = Config{} - mi := &file_transport_internet_splithttp_config_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Config) ProtoMessage() {} - -func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_splithttp_config_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Config.ProtoReflect.Descriptor instead. -func (*Config) Descriptor() ([]byte, []int) { - return file_transport_internet_splithttp_config_proto_rawDescGZIP(), []int{2} -} - -func (x *Config) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *Config) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *Config) GetMode() string { - if x != nil { - return x.Mode - } - return "" -} - -func (x *Config) GetHeaders() map[string]string { - if x != nil { - return x.Headers - } - return nil -} - -func (x *Config) GetXPaddingBytes() *RangeConfig { - if x != nil { - return x.XPaddingBytes - } - return nil -} - -func (x *Config) GetNoGRPCHeader() bool { - if x != nil { - return x.NoGRPCHeader - } - return false -} - -func (x *Config) GetNoSSEHeader() bool { - if x != nil { - return x.NoSSEHeader - } - return false -} - -func (x *Config) GetScMaxEachPostBytes() *RangeConfig { - if x != nil { - return x.ScMaxEachPostBytes - } - return nil -} - -func (x *Config) GetScMinPostsIntervalMs() *RangeConfig { - if x != nil { - return x.ScMinPostsIntervalMs - } - return nil -} - -func (x *Config) GetScMaxBufferedPosts() int64 { - if x != nil { - return x.ScMaxBufferedPosts - } - return 0 -} - -func (x *Config) GetScStreamUpServerSecs() *RangeConfig { - if x != nil { - return x.ScStreamUpServerSecs - } - return nil -} - -func (x *Config) GetXmux() *XmuxConfig { - if x != nil { - return x.Xmux - } - return nil -} - -func (x *Config) GetDownloadSettings() *internet.StreamConfig { - if x != nil { - return x.DownloadSettings - } - return nil -} - -var File_transport_internet_splithttp_config_proto protoreflect.FileDescriptor - -var file_transport_internet_splithttp_config_proto_rawDesc = []byte{ - 0x0a, 0x29, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x1a, 0x1f, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, - 0x31, 0x0a, 0x0b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, - 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72, - 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, - 0x74, 0x6f, 0x22, 0xf8, 0x03, 0x0a, 0x0a, 0x58, 0x6d, 0x75, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x56, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, - 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x56, 0x0a, 0x0e, 0x6d, 0x61, 0x78, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2e, 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, 0x73, 0x70, 0x6c, 0x69, - 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x56, 0x0a, 0x0e, 0x63, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x65, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x63, 0x4d, 0x61, 0x78, 0x52, - 0x65, 0x75, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x10, 0x68, 0x4d, 0x61, - 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x73, 0x70, - 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2e, 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, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, - 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, - 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x68, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x68, 0x4b, 0x65, - 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xdc, 0x06, - 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x50, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 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, - 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x54, 0x0a, 0x0d, 0x78, 0x50, 0x61, 0x64, 0x64, 0x69, - 0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, - 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x78, - 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0c, - 0x6e, 0x6f, 0x47, 0x52, 0x50, 0x43, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x47, 0x52, 0x50, 0x43, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x12, 0x5e, 0x0a, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x45, 0x61, 0x63, 0x68, 0x50, - 0x6f, 0x73, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, - 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, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, - 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x12, - 0x73, 0x63, 0x4d, 0x61, 0x78, 0x45, 0x61, 0x63, 0x68, 0x50, 0x6f, 0x73, 0x74, 0x42, 0x79, 0x74, - 0x65, 0x73, 0x12, 0x62, 0x0a, 0x14, 0x73, 0x63, 0x4d, 0x69, 0x6e, 0x50, 0x6f, 0x73, 0x74, 0x73, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2e, 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, 0x73, 0x70, 0x6c, 0x69, 0x74, - 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x14, 0x73, 0x63, 0x4d, 0x69, 0x6e, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65, - 0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x62, 0x0a, 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x55, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x63, 0x73, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x73, - 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x70, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x63, 0x73, 0x12, 0x41, 0x0a, 0x04, 0x78, 0x6d, - 0x75, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x58, 0x6d, 0x75, - 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x04, 0x78, 0x6d, 0x75, 0x78, 0x12, 0x51, 0x0a, - 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x18, 0x0d, 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, 0x10, - 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x85, 0x01, 0x0a, - 0x25, 0x63, 0x6f, 0x6d, 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, 0x73, 0x70, 0x6c, - 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, - 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, - 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_transport_internet_splithttp_config_proto_rawDescOnce sync.Once - file_transport_internet_splithttp_config_proto_rawDescData = file_transport_internet_splithttp_config_proto_rawDesc -) - -func file_transport_internet_splithttp_config_proto_rawDescGZIP() []byte { - file_transport_internet_splithttp_config_proto_rawDescOnce.Do(func() { - file_transport_internet_splithttp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_splithttp_config_proto_rawDescData) - }) - return file_transport_internet_splithttp_config_proto_rawDescData -} - -var file_transport_internet_splithttp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_transport_internet_splithttp_config_proto_goTypes = []any{ - (*RangeConfig)(nil), // 0: xray.transport.internet.splithttp.RangeConfig - (*XmuxConfig)(nil), // 1: xray.transport.internet.splithttp.XmuxConfig - (*Config)(nil), // 2: xray.transport.internet.splithttp.Config - nil, // 3: xray.transport.internet.splithttp.Config.HeadersEntry - (*internet.StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig -} -var file_transport_internet_splithttp_config_proto_depIdxs = []int32{ - 0, // 0: xray.transport.internet.splithttp.XmuxConfig.maxConcurrency:type_name -> xray.transport.internet.splithttp.RangeConfig - 0, // 1: xray.transport.internet.splithttp.XmuxConfig.maxConnections:type_name -> xray.transport.internet.splithttp.RangeConfig - 0, // 2: xray.transport.internet.splithttp.XmuxConfig.cMaxReuseTimes:type_name -> xray.transport.internet.splithttp.RangeConfig - 0, // 3: xray.transport.internet.splithttp.XmuxConfig.hMaxRequestTimes:type_name -> xray.transport.internet.splithttp.RangeConfig - 0, // 4: xray.transport.internet.splithttp.XmuxConfig.hMaxReusableSecs:type_name -> xray.transport.internet.splithttp.RangeConfig - 3, // 5: xray.transport.internet.splithttp.Config.headers:type_name -> xray.transport.internet.splithttp.Config.HeadersEntry - 0, // 6: xray.transport.internet.splithttp.Config.xPaddingBytes:type_name -> xray.transport.internet.splithttp.RangeConfig - 0, // 7: xray.transport.internet.splithttp.Config.scMaxEachPostBytes:type_name -> xray.transport.internet.splithttp.RangeConfig - 0, // 8: xray.transport.internet.splithttp.Config.scMinPostsIntervalMs:type_name -> xray.transport.internet.splithttp.RangeConfig - 0, // 9: xray.transport.internet.splithttp.Config.scStreamUpServerSecs:type_name -> xray.transport.internet.splithttp.RangeConfig - 1, // 10: xray.transport.internet.splithttp.Config.xmux:type_name -> xray.transport.internet.splithttp.XmuxConfig - 4, // 11: xray.transport.internet.splithttp.Config.downloadSettings:type_name -> xray.transport.internet.StreamConfig - 12, // [12:12] is the sub-list for method output_type - 12, // [12:12] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name -} - -func init() { file_transport_internet_splithttp_config_proto_init() } -func file_transport_internet_splithttp_config_proto_init() { - if File_transport_internet_splithttp_config_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_transport_internet_splithttp_config_proto_rawDesc, - NumEnums: 0, - NumMessages: 4, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_transport_internet_splithttp_config_proto_goTypes, - DependencyIndexes: file_transport_internet_splithttp_config_proto_depIdxs, - MessageInfos: file_transport_internet_splithttp_config_proto_msgTypes, - }.Build() - File_transport_internet_splithttp_config_proto = out.File - file_transport_internet_splithttp_config_proto_rawDesc = nil - file_transport_internet_splithttp_config_proto_goTypes = nil - file_transport_internet_splithttp_config_proto_depIdxs = nil -} diff --git a/transport/internet/splithttp/config.proto b/transport/internet/splithttp/config.proto deleted file mode 100644 index beb1d004..00000000 --- a/transport/internet/splithttp/config.proto +++ /dev/null @@ -1,39 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.splithttp; -option csharp_namespace = "Xray.Transport.Internet.SplitHttp"; -option go_package = "github.com/xtls/xray-core/transport/internet/splithttp"; -option java_package = "com.xray.transport.internet.splithttp"; -option java_multiple_files = true; - -import "transport/internet/config.proto"; - -message RangeConfig { - int32 from = 1; - int32 to = 2; -} - -message XmuxConfig { - RangeConfig maxConcurrency = 1; - RangeConfig maxConnections = 2; - RangeConfig cMaxReuseTimes = 3; - RangeConfig hMaxRequestTimes = 4; - RangeConfig hMaxReusableSecs = 5; - int64 hKeepAlivePeriod = 6; -} - -message Config { - string host = 1; - string path = 2; - string mode = 3; - map headers = 4; - RangeConfig xPaddingBytes = 5; - bool noGRPCHeader = 6; - bool noSSEHeader = 7; - RangeConfig scMaxEachPostBytes = 8; - RangeConfig scMinPostsIntervalMs = 9; - int64 scMaxBufferedPosts = 10; - RangeConfig scStreamUpServerSecs = 11; - XmuxConfig xmux = 12; - xray.transport.internet.StreamConfig downloadSettings = 13; -} diff --git a/transport/internet/splithttp/config_test.go b/transport/internet/splithttp/config_test.go deleted file mode 100644 index 39c3fd95..00000000 --- a/transport/internet/splithttp/config_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package splithttp_test - -import ( - "testing" - - . "github.com/xtls/xray-core/transport/internet/splithttp" -) - -func Test_GetNormalizedPath(t *testing.T) { - c := Config{ - Path: "/?world", - } - - path := c.GetNormalizedPath() - if path != "/" { - t.Error("Unexpected: ", path) - } -} diff --git a/transport/internet/splithttp/connection.go b/transport/internet/splithttp/connection.go deleted file mode 100644 index 613e1f36..00000000 --- a/transport/internet/splithttp/connection.go +++ /dev/null @@ -1,64 +0,0 @@ -package splithttp - -import ( - "io" - "net" - "time" -) - -type splitConn struct { - writer io.WriteCloser - reader io.ReadCloser - remoteAddr net.Addr - localAddr net.Addr - onClose func() -} - -func (c *splitConn) Write(b []byte) (int, error) { - return c.writer.Write(b) -} - -func (c *splitConn) Read(b []byte) (int, error) { - return c.reader.Read(b) -} - -func (c *splitConn) Close() error { - if c.onClose != nil { - c.onClose() - } - - err := c.writer.Close() - err2 := c.reader.Close() - if err != nil { - return err - } - - if err2 != nil { - return err - } - - return nil -} - -func (c *splitConn) LocalAddr() net.Addr { - return c.localAddr -} - -func (c *splitConn) RemoteAddr() net.Addr { - return c.remoteAddr -} - -func (c *splitConn) SetDeadline(t time.Time) error { - // TODO cannot do anything useful - return nil -} - -func (c *splitConn) SetReadDeadline(t time.Time) error { - // TODO cannot do anything useful - return nil -} - -func (c *splitConn) SetWriteDeadline(t time.Time) error { - // TODO cannot do anything useful - return nil -} diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go deleted file mode 100644 index c5fba78b..00000000 --- a/transport/internet/splithttp/dialer.go +++ /dev/null @@ -1,503 +0,0 @@ -package splithttp - -import ( - "context" - gotls "crypto/tls" - "fmt" - "io" - "net/http" - "net/http/httptrace" - "net/url" - "strconv" - "sync" - "sync/atomic" - "time" - - "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/http3" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/signal/done" - "github.com/xtls/xray-core/common/uuid" - "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/browser_dialer" - "github.com/xtls/xray-core/transport/internet/reality" - "github.com/xtls/xray-core/transport/internet/stat" - "github.com/xtls/xray-core/transport/internet/tls" - "github.com/xtls/xray-core/transport/pipe" - "golang.org/x/net/http2" -) - -type dialerConf struct { - net.Destination - *internet.MemoryStreamConfig -} - -var ( - globalDialerMap map[dialerConf]*XmuxManager - globalDialerAccess sync.Mutex -) - -func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (DialerClient, *XmuxClient) { - realityConfig := reality.ConfigFromStreamSettings(streamSettings) - - if browser_dialer.HasBrowserDialer() && realityConfig == nil { - return &BrowserDialerClient{transportConfig: streamSettings.ProtocolSettings.(*Config)}, nil - } - - globalDialerAccess.Lock() - defer globalDialerAccess.Unlock() - - if globalDialerMap == nil { - globalDialerMap = make(map[dialerConf]*XmuxManager) - } - - key := dialerConf{dest, streamSettings} - - xmuxManager, found := globalDialerMap[key] - - if !found { - transportConfig := streamSettings.ProtocolSettings.(*Config) - var xmuxConfig XmuxConfig - if transportConfig.Xmux != nil { - xmuxConfig = *transportConfig.Xmux - } - - xmuxManager = NewXmuxManager(xmuxConfig, func() XmuxConn { - return createHTTPClient(dest, streamSettings) - }) - globalDialerMap[key] = xmuxManager - } - - xmuxClient := xmuxManager.GetXmuxClient(ctx) - return xmuxClient.XmuxConn.(DialerClient), xmuxClient -} - -func decideHTTPVersion(tlsConfig *tls.Config, realityConfig *reality.Config) string { - if realityConfig != nil { - return "2" - } - if tlsConfig == nil { - return "1.1" - } - if len(tlsConfig.NextProtocol) != 1 { - return "2" - } - if tlsConfig.NextProtocol[0] == "http/1.1" { - return "1.1" - } - if tlsConfig.NextProtocol[0] == "h3" { - return "3" - } - return "2" -} - -func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient { - tlsConfig := tls.ConfigFromStreamSettings(streamSettings) - realityConfig := reality.ConfigFromStreamSettings(streamSettings) - - httpVersion := decideHTTPVersion(tlsConfig, realityConfig) - if httpVersion == "3" { - dest.Network = net.Network_UDP // better to keep this line - } - - var gotlsConfig *gotls.Config - - if tlsConfig != nil { - gotlsConfig = tlsConfig.GetTLSConfig(tls.WithDestination(dest)) - } - - transportConfig := streamSettings.ProtocolSettings.(*Config) - - dialContext := func(ctxInner context.Context) (net.Conn, error) { - conn, err := internet.DialSystem(ctxInner, dest, streamSettings.SocketSettings) - if err != nil { - return nil, err - } - - if realityConfig != nil { - return reality.UClient(conn, realityConfig, ctxInner, dest) - } - - if gotlsConfig != nil { - if fingerprint := tls.GetFingerprint(tlsConfig.Fingerprint); fingerprint != nil { - conn = tls.UClient(conn, gotlsConfig, fingerprint) - if err := conn.(*tls.UConn).HandshakeContext(ctxInner); err != nil { - return nil, err - } - } else { - conn = tls.Client(conn, gotlsConfig) - } - } - - return conn, nil - } - - var keepAlivePeriod time.Duration - if streamSettings.ProtocolSettings.(*Config).Xmux != nil { - keepAlivePeriod = time.Duration(streamSettings.ProtocolSettings.(*Config).Xmux.HKeepAlivePeriod) * time.Second - } - - var transport http.RoundTripper - - if httpVersion == "3" { - if keepAlivePeriod == 0 { - keepAlivePeriod = net.QuicgoH3KeepAlivePeriod - } - if keepAlivePeriod < 0 { - keepAlivePeriod = 0 - } - quicConfig := &quic.Config{ - MaxIdleTimeout: net.ConnIdleTimeout, - - // these two are defaults of quic-go/http3. the default of quic-go (no - // http3) is different, so it is hardcoded here for clarity. - // https://github.com/quic-go/quic-go/blob/b8ea5c798155950fb5bbfdd06cad1939c9355878/http3/client.go#L36-L39 - MaxIncomingStreams: -1, - KeepAlivePeriod: keepAlivePeriod, - } - transport = &http3.Transport{ - QUICConfig: quicConfig, - TLSClientConfig: gotlsConfig, - Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { - conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) - if err != nil { - return nil, err - } - - var udpConn net.PacketConn - var udpAddr *net.UDPAddr - - switch c := conn.(type) { - case *internet.PacketConnWrapper: - var ok bool - udpConn, ok = c.Conn.(*net.UDPConn) - if !ok { - return nil, errors.New("PacketConnWrapper does not contain a UDP connection") - } - udpAddr, err = net.ResolveUDPAddr("udp", c.Dest.String()) - if err != nil { - return nil, err - } - case *net.UDPConn: - udpConn = c - udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String()) - if err != nil { - return nil, err - } - default: - udpConn = &internet.FakePacketConn{Conn: c} - udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String()) - if err != nil { - return nil, err - } - } - - return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg) - }, - } - } else if httpVersion == "2" { - if keepAlivePeriod == 0 { - keepAlivePeriod = net.ChromeH2KeepAlivePeriod - } - if keepAlivePeriod < 0 { - keepAlivePeriod = 0 - } - transport = &http2.Transport{ - DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) { - return dialContext(ctxInner) - }, - IdleConnTimeout: net.ConnIdleTimeout, - ReadIdleTimeout: keepAlivePeriod, - } - } else { - httpDialContext := func(ctxInner context.Context, network string, addr string) (net.Conn, error) { - return dialContext(ctxInner) - } - - transport = &http.Transport{ - DialTLSContext: httpDialContext, - DialContext: httpDialContext, - IdleConnTimeout: net.ConnIdleTimeout, - // chunked transfer download with KeepAlives is buggy with - // http.Client and our custom dial context. - DisableKeepAlives: true, - } - } - - client := &DefaultDialerClient{ - transportConfig: transportConfig, - client: &http.Client{ - Transport: transport, - }, - httpVersion: httpVersion, - uploadRawPool: &sync.Pool{}, - dialUploadConn: dialContext, - } - - return client -} - -func init() { - common.Must(internet.RegisterTransportDialer(protocolName, Dial)) -} - -func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { - tlsConfig := tls.ConfigFromStreamSettings(streamSettings) - realityConfig := reality.ConfigFromStreamSettings(streamSettings) - - httpVersion := decideHTTPVersion(tlsConfig, realityConfig) - if httpVersion == "3" { - dest.Network = net.Network_UDP - } - - transportConfiguration := streamSettings.ProtocolSettings.(*Config) - var requestURL url.URL - - if tlsConfig != nil || realityConfig != nil { - requestURL.Scheme = "https" - } else { - requestURL.Scheme = "http" - } - requestURL.Host = transportConfiguration.Host - if requestURL.Host == "" && tlsConfig != nil { - requestURL.Host = tlsConfig.ServerName - } - if requestURL.Host == "" && realityConfig != nil { - requestURL.Host = realityConfig.ServerName - } - if requestURL.Host == "" { - requestURL.Host = dest.Address.String() - } - - sessionIdUuid := uuid.New() - requestURL.Path = transportConfiguration.GetNormalizedPath() + sessionIdUuid.String() - requestURL.RawQuery = transportConfiguration.GetNormalizedQuery() - - httpClient, xmuxClient := getHTTPClient(ctx, dest, streamSettings) - - mode := transportConfiguration.Mode - if mode == "" || mode == "auto" { - mode = "packet-up" - if realityConfig != nil { - mode = "stream-one" - if transportConfiguration.DownloadSettings != nil { - mode = "stream-up" - } - } - } - - errors.LogInfo(ctx, fmt.Sprintf("XHTTP is dialing to %s, mode %s, HTTP version %s, host %s", dest, mode, httpVersion, requestURL.Host)) - - requestURL2 := requestURL - httpClient2 := httpClient - xmuxClient2 := xmuxClient - if transportConfiguration.DownloadSettings != nil { - globalDialerAccess.Lock() - if streamSettings.DownloadSettings == nil { - streamSettings.DownloadSettings = common.Must2(internet.ToMemoryStreamConfig(transportConfiguration.DownloadSettings)).(*internet.MemoryStreamConfig) - if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.Penetrate { - streamSettings.DownloadSettings.SocketSettings = streamSettings.SocketSettings - } - } - globalDialerAccess.Unlock() - memory2 := streamSettings.DownloadSettings - dest2 := *memory2.Destination // just panic - tlsConfig2 := tls.ConfigFromStreamSettings(memory2) - realityConfig2 := reality.ConfigFromStreamSettings(memory2) - httpVersion2 := decideHTTPVersion(tlsConfig2, realityConfig2) - if httpVersion2 == "3" { - dest2.Network = net.Network_UDP - } - if tlsConfig2 != nil || realityConfig2 != nil { - requestURL2.Scheme = "https" - } else { - requestURL2.Scheme = "http" - } - config2 := memory2.ProtocolSettings.(*Config) - requestURL2.Host = config2.Host - if requestURL2.Host == "" && tlsConfig2 != nil { - requestURL2.Host = tlsConfig2.ServerName - } - if requestURL2.Host == "" && realityConfig2 != nil { - requestURL2.Host = realityConfig2.ServerName - } - if requestURL2.Host == "" { - requestURL2.Host = dest2.Address.String() - } - requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String() - requestURL2.RawQuery = config2.GetNormalizedQuery() - httpClient2, xmuxClient2 = getHTTPClient(ctx, dest2, memory2) - errors.LogInfo(ctx, fmt.Sprintf("XHTTP is downloading from %s, mode %s, HTTP version %s, host %s", dest2, "stream-down", httpVersion2, requestURL2.Host)) - } - - if xmuxClient != nil { - xmuxClient.OpenUsage.Add(1) - } - if xmuxClient2 != nil && xmuxClient2 != xmuxClient { - xmuxClient2.OpenUsage.Add(1) - } - var closed atomic.Int32 - - reader, writer := io.Pipe() - conn := splitConn{ - writer: writer, - onClose: func() { - if closed.Add(1) > 1 { - return - } - if xmuxClient != nil { - xmuxClient.OpenUsage.Add(-1) - } - if xmuxClient2 != nil && xmuxClient2 != xmuxClient { - xmuxClient2.OpenUsage.Add(-1) - } - }, - } - - var err error - if mode == "stream-one" { - requestURL.Path = transportConfiguration.GetNormalizedPath() - if xmuxClient != nil { - xmuxClient.LeftRequests.Add(-1) - } - conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient.OpenStream(ctx, requestURL.String(), reader, false) - if err != nil { // browser dialer only - return nil, err - } - return stat.Connection(&conn), nil - } else { // stream-down - if xmuxClient2 != nil { - xmuxClient2.LeftRequests.Add(-1) - } - conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(ctx, requestURL2.String(), nil, false) - if err != nil { // browser dialer only - return nil, err - } - } - if mode == "stream-up" { - if xmuxClient != nil { - xmuxClient.LeftRequests.Add(-1) - } - _, _, _, err = httpClient.OpenStream(ctx, requestURL.String(), reader, true) - if err != nil { // browser dialer only - return nil, err - } - return stat.Connection(&conn), nil - } - - scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes() - scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs() - - if scMaxEachPostBytes.From <= buf.Size { - panic("`scMaxEachPostBytes` should be bigger than " + strconv.Itoa(buf.Size)) - } - - maxUploadSize := scMaxEachPostBytes.rand() - // WithSizeLimit(0) will still allow single bytes to pass, and a lot of - // code relies on this behavior. Subtract 1 so that together with - // uploadWriter wrapper, exact size limits can be enforced - // uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize - 1)) - uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize - buf.Size)) - - conn.writer = uploadWriter{ - uploadPipeWriter, - maxUploadSize, - } - - go func() { - var seq int64 - var lastWrite time.Time - - for { - wroteRequest := done.New() - - ctx := httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{ - WroteRequest: func(httptrace.WroteRequestInfo) { - wroteRequest.Close() - }, - }) - - // this intentionally makes a shallow-copy of the struct so we - // can reassign Path (potentially concurrently) - url := requestURL - url.Path += "/" + strconv.FormatInt(seq, 10) - - seq += 1 - - if scMinPostsIntervalMs.From > 0 { - time.Sleep(time.Duration(scMinPostsIntervalMs.rand())*time.Millisecond - time.Since(lastWrite)) - } - - // by offloading the uploads into a buffered pipe, multiple conn.Write - // calls get automatically batched together into larger POST requests. - // without batching, bandwidth is extremely limited. - chunk, err := uploadPipeReader.ReadMultiBuffer() - if err != nil { - break - } - - lastWrite = time.Now() - - if xmuxClient != nil && (xmuxClient.LeftRequests.Add(-1) <= 0 || - (xmuxClient.UnreusableAt != time.Time{} && lastWrite.After(xmuxClient.UnreusableAt))) { - httpClient, xmuxClient = getHTTPClient(ctx, dest, streamSettings) - } - - go func() { - err := httpClient.PostPacket( - ctx, - url.String(), - &buf.MultiBufferContainer{MultiBuffer: chunk}, - int64(chunk.Len()), - ) - wroteRequest.Close() - if err != nil { - errors.LogInfoInner(ctx, err, "failed to send upload") - uploadPipeReader.Interrupt() - } - }() - - if _, ok := httpClient.(*DefaultDialerClient); ok { - <-wroteRequest.Wait() - } - } - }() - - return stat.Connection(&conn), nil -} - -// A wrapper around pipe that ensures the size limit is exactly honored. -// -// The MultiBuffer pipe accepts any single WriteMultiBuffer call even if that -// single MultiBuffer exceeds the size limit, and then starts blocking on the -// next WriteMultiBuffer call. This means that ReadMultiBuffer can return more -// bytes than the size limit. We work around this by splitting a potentially -// too large write up into multiple. -type uploadWriter struct { - *pipe.Writer - maxLen int32 -} - -func (w uploadWriter) Write(b []byte) (int, error) { - /* - capacity := int(w.maxLen - w.Len()) - if capacity > 0 && capacity < len(b) { - b = b[:capacity] - } - */ - - buffer := buf.New() - n, err := buffer.Write(b) - if err != nil { - return 0, err - } - - err = w.WriteMultiBuffer([]*buf.Buffer{buffer}) - if err != nil { - return 0, err - } - return n, nil -} diff --git a/transport/internet/splithttp/h1_conn.go b/transport/internet/splithttp/h1_conn.go deleted file mode 100644 index f89f2a66..00000000 --- a/transport/internet/splithttp/h1_conn.go +++ /dev/null @@ -1,19 +0,0 @@ -package splithttp - -import ( - "bufio" - "net" -) - -type H1Conn struct { - UnreadedResponsesCount int - RespBufReader *bufio.Reader - net.Conn -} - -func NewH1Conn(conn net.Conn) *H1Conn { - return &H1Conn{ - RespBufReader: bufio.NewReader(conn), - Conn: conn, - } -} diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go deleted file mode 100644 index d161741a..00000000 --- a/transport/internet/splithttp/hub.go +++ /dev/null @@ -1,478 +0,0 @@ -package splithttp - -import ( - "bytes" - "context" - gotls "crypto/tls" - "io" - "net/http" - "net/url" - "strconv" - "strings" - "sync" - "time" - - "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/http3" - goreality "github.com/xtls/reality" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/net" - http_proto "github.com/xtls/xray-core/common/protocol/http" - "github.com/xtls/xray-core/common/signal/done" - "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/reality" - "github.com/xtls/xray-core/transport/internet/stat" - "github.com/xtls/xray-core/transport/internet/tls" -) - -type requestHandler struct { - config *Config - host string - path string - ln *Listener - sessionMu *sync.Mutex - sessions sync.Map - localAddr net.Addr -} - -type httpSession struct { - uploadQueue *uploadQueue - // for as long as the GET request is not opened by the client, this will be - // open ("undone"), and the session may be expired within a certain TTL. - // after the client connects, this becomes "done" and the session lives as - // long as the GET request. - isFullyConnected *done.Instance -} - -func (h *requestHandler) upsertSession(sessionId string) *httpSession { - // fast path - currentSessionAny, ok := h.sessions.Load(sessionId) - if ok { - return currentSessionAny.(*httpSession) - } - - // slow path - h.sessionMu.Lock() - defer h.sessionMu.Unlock() - - currentSessionAny, ok = h.sessions.Load(sessionId) - if ok { - return currentSessionAny.(*httpSession) - } - - s := &httpSession{ - uploadQueue: NewUploadQueue(h.ln.config.GetNormalizedScMaxBufferedPosts()), - isFullyConnected: done.New(), - } - - h.sessions.Store(sessionId, s) - - shouldReap := done.New() - go func() { - time.Sleep(30 * time.Second) - shouldReap.Close() - }() - go func() { - select { - case <-shouldReap.Wait(): - h.sessions.Delete(sessionId) - s.uploadQueue.Close() - case <-s.isFullyConnected.Wait(): - } - }() - - return s -} - -func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { - if len(h.host) > 0 && !internet.IsValidHTTPHost(request.Host, h.host) { - errors.LogInfo(context.Background(), "failed to validate host, request:", request.Host, ", config:", h.host) - writer.WriteHeader(http.StatusNotFound) - return - } - - if !strings.HasPrefix(request.URL.Path, h.path) { - errors.LogInfo(context.Background(), "failed to validate path, request:", request.URL.Path, ", config:", h.path) - writer.WriteHeader(http.StatusNotFound) - return - } - - h.config.WriteResponseHeader(writer) - - /* - clientVer := []int{0, 0, 0} - x_version := strings.Split(request.URL.Query().Get("x_version"), ".") - for j := 0; j < 3 && len(x_version) > j; j++ { - clientVer[j], _ = strconv.Atoi(x_version[j]) - } - */ - - validRange := h.config.GetNormalizedXPaddingBytes() - paddingLength := 0 - - referrer := request.Header.Get("Referer") - if referrer != "" { - if referrerURL, err := url.Parse(referrer); err == nil { - // Browser dialer cannot control the host part of referrer header, so only check the query - paddingLength = len(referrerURL.Query().Get("x_padding")) - } - } else { - paddingLength = len(request.URL.Query().Get("x_padding")) - } - - if int32(paddingLength) < validRange.From || int32(paddingLength) > validRange.To { - errors.LogInfo(context.Background(), "invalid x_padding length:", int32(paddingLength)) - writer.WriteHeader(http.StatusBadRequest) - return - } - - sessionId := "" - subpath := strings.Split(request.URL.Path[len(h.path):], "/") - if len(subpath) > 0 { - sessionId = subpath[0] - } - - if sessionId == "" && h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "stream-one" && h.config.Mode != "stream-up" { - errors.LogInfo(context.Background(), "stream-one mode is not allowed") - writer.WriteHeader(http.StatusBadRequest) - return - } - - forwardedAddrs := http_proto.ParseXForwardedFor(request.Header) - var remoteAddr net.Addr - var err error - remoteAddr, err = net.ResolveTCPAddr("tcp", request.RemoteAddr) - if err != nil { - remoteAddr = &net.TCPAddr{ - IP: []byte{0, 0, 0, 0}, - Port: 0, - } - } - if request.ProtoMajor == 3 { - remoteAddr = &net.UDPAddr{ - IP: remoteAddr.(*net.TCPAddr).IP, - Port: remoteAddr.(*net.TCPAddr).Port, - } - } - if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { - remoteAddr = &net.TCPAddr{ - IP: forwardedAddrs[0].IP(), - Port: 0, - } - } - - var currentSession *httpSession - if sessionId != "" { - currentSession = h.upsertSession(sessionId) - } - scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To) - - if request.Method == "POST" && sessionId != "" { // stream-up, packet-up - seq := "" - if len(subpath) > 1 { - seq = subpath[1] - } - - if seq == "" { - if h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "stream-up" { - errors.LogInfo(context.Background(), "stream-up mode is not allowed") - writer.WriteHeader(http.StatusBadRequest) - return - } - httpSC := &httpServerConn{ - Instance: done.New(), - Reader: request.Body, - ResponseWriter: writer, - } - err = currentSession.uploadQueue.Push(Packet{ - Reader: httpSC, - }) - if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to upload (PushReader)") - writer.WriteHeader(http.StatusConflict) - } else { - writer.Header().Set("X-Accel-Buffering", "no") - writer.Header().Set("Cache-Control", "no-store") - writer.WriteHeader(http.StatusOK) - scStreamUpServerSecs := h.config.GetNormalizedScStreamUpServerSecs() - if referrer != "" && scStreamUpServerSecs.To > 0 { - go func() { - for { - _, err := httpSC.Write(bytes.Repeat([]byte{'X'}, int(h.config.GetNormalizedXPaddingBytes().rand()))) - if err != nil { - break - } - time.Sleep(time.Duration(scStreamUpServerSecs.rand()) * time.Second) - } - }() - } - select { - case <-request.Context().Done(): - case <-httpSC.Wait(): - } - } - httpSC.Close() - return - } - - if h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "packet-up" { - errors.LogInfo(context.Background(), "packet-up mode is not allowed") - writer.WriteHeader(http.StatusBadRequest) - return - } - - payload, err := io.ReadAll(io.LimitReader(request.Body, int64(scMaxEachPostBytes)+1)) - - if len(payload) > scMaxEachPostBytes { - errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request size exceed it. Adjust scMaxEachPostBytes on the server to be at least as large as client.") - writer.WriteHeader(http.StatusRequestEntityTooLarge) - return - } - - if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to upload (ReadAll)") - writer.WriteHeader(http.StatusInternalServerError) - return - } - - seqInt, err := strconv.ParseUint(seq, 10, 64) - if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to upload (ParseUint)") - writer.WriteHeader(http.StatusInternalServerError) - return - } - - err = currentSession.uploadQueue.Push(Packet{ - Payload: payload, - Seq: seqInt, - }) - - if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to upload (PushPayload)") - writer.WriteHeader(http.StatusInternalServerError) - return - } - - writer.WriteHeader(http.StatusOK) - } else if request.Method == "GET" || sessionId == "" { // stream-down, stream-one - if sessionId != "" { - // after GET is done, the connection is finished. disable automatic - // session reaping, and handle it in defer - currentSession.isFullyConnected.Close() - defer h.sessions.Delete(sessionId) - } - - // magic header instructs nginx + apache to not buffer response body - writer.Header().Set("X-Accel-Buffering", "no") - // A web-compliant header telling all middleboxes to disable caching. - // Should be able to prevent overloading the cache, or stop CDNs from - // teeing the response stream into their cache, causing slowdowns. - writer.Header().Set("Cache-Control", "no-store") - - if !h.config.NoSSEHeader { - // magic header to make the HTTP middle box consider this as SSE to disable buffer - writer.Header().Set("Content-Type", "text/event-stream") - } - - writer.WriteHeader(http.StatusOK) - writer.(http.Flusher).Flush() - - httpSC := &httpServerConn{ - Instance: done.New(), - Reader: request.Body, - ResponseWriter: writer, - } - conn := splitConn{ - writer: httpSC, - reader: httpSC, - remoteAddr: remoteAddr, - localAddr: h.localAddr, - } - if sessionId != "" { // if not stream-one - conn.reader = currentSession.uploadQueue - } - - h.ln.addConn(stat.Connection(&conn)) - - // "A ResponseWriter may not be used after [Handler.ServeHTTP] has returned." - select { - case <-request.Context().Done(): - case <-httpSC.Wait(): - } - - conn.Close() - } else { - errors.LogInfo(context.Background(), "unsupported method: ", request.Method) - writer.WriteHeader(http.StatusMethodNotAllowed) - } -} - -type httpServerConn struct { - sync.Mutex - *done.Instance - io.Reader // no need to Close request.Body - http.ResponseWriter -} - -func (c *httpServerConn) Write(b []byte) (int, error) { - c.Lock() - defer c.Unlock() - if c.Done() { - return 0, io.ErrClosedPipe - } - n, err := c.ResponseWriter.Write(b) - if err == nil { - c.ResponseWriter.(http.Flusher).Flush() - } - return n, err -} - -func (c *httpServerConn) Close() error { - c.Lock() - defer c.Unlock() - return c.Instance.Close() -} - -type Listener struct { - sync.Mutex - server http.Server - h3server *http3.Server - listener net.Listener - h3listener *quic.EarlyListener - config *Config - addConn internet.ConnHandler - isH3 bool -} - -func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { - l := &Listener{ - addConn: addConn, - } - l.config = streamSettings.ProtocolSettings.(*Config) - if l.config != nil { - if streamSettings.SocketSettings == nil { - streamSettings.SocketSettings = &internet.SocketConfig{} - } - } - handler := &requestHandler{ - config: l.config, - host: l.config.Host, - path: l.config.GetNormalizedPath(), - ln: l, - sessionMu: &sync.Mutex{}, - sessions: sync.Map{}, - } - tlsConfig := getTLSConfig(streamSettings) - l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3" - - var err error - if port == net.Port(0) { // unix - l.listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ - Name: address.Domain(), - Net: "unix", - }, streamSettings.SocketSettings) - if err != nil { - return nil, errors.New("failed to listen UNIX domain socket for XHTTP on ", address).Base(err) - } - errors.LogInfo(ctx, "listening UNIX domain socket for XHTTP on ", address) - } else if l.isH3 { // quic - Conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ - IP: address.IP(), - Port: int(port), - }, streamSettings.SocketSettings) - if err != nil { - return nil, errors.New("failed to listen UDP for XHTTP/3 on ", address, ":", port).Base(err) - } - l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, nil) - if err != nil { - return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err) - } - errors.LogInfo(ctx, "listening QUIC for XHTTP/3 on ", address, ":", port) - - handler.localAddr = l.h3listener.Addr() - - l.h3server = &http3.Server{ - Handler: handler, - } - go func() { - if err := l.h3server.ServeListener(l.h3listener); err != nil { - errors.LogErrorInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3") - } - }() - } else { // tcp - l.listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ - IP: address.IP(), - Port: int(port), - }, streamSettings.SocketSettings) - if err != nil { - return nil, errors.New("failed to listen TCP for XHTTP on ", address, ":", port).Base(err) - } - errors.LogInfo(ctx, "listening TCP for XHTTP on ", address, ":", port) - } - - // tcp/unix (h1/h2) - if l.listener != nil { - if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { - if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { - l.listener = gotls.NewListener(l.listener, tlsConfig) - } - } - if config := reality.ConfigFromStreamSettings(streamSettings); config != nil { - l.listener = goreality.NewListener(l.listener, config.GetREALITYConfig()) - } - - handler.localAddr = l.listener.Addr() - - // server can handle both plaintext HTTP/1.1 and h2c - protocols := new(http.Protocols) - protocols.SetHTTP1(true) - protocols.SetUnencryptedHTTP2(true) - l.server = http.Server{ - Handler: handler, - ReadHeaderTimeout: time.Second * 4, - MaxHeaderBytes: 8192, - Protocols: protocols, - } - go func() { - if err := l.server.Serve(l.listener); err != nil { - errors.LogErrorInner(ctx, err, "failed to serve HTTP for XHTTP") - } - }() - } - - return l, err -} - -// Addr implements net.Listener.Addr(). -func (ln *Listener) Addr() net.Addr { - if ln.h3listener != nil { - return ln.h3listener.Addr() - } - if ln.listener != nil { - return ln.listener.Addr() - } - return nil -} - -// Close implements net.Listener.Close(). -func (ln *Listener) Close() error { - if ln.h3server != nil { - if err := ln.h3server.Close(); err != nil { - return err - } - } else if ln.listener != nil { - return ln.listener.Close() - } - return errors.New("listener does not have an HTTP/3 server or a net.listener") -} -func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *gotls.Config { - config := tls.ConfigFromStreamSettings(streamSettings) - if config == nil { - return &gotls.Config{} - } - return config.GetTLSConfig() -} -func init() { - common.Must(internet.RegisterTransportListener(protocolName, ListenXH)) -} diff --git a/transport/internet/splithttp/mux.go b/transport/internet/splithttp/mux.go deleted file mode 100644 index 093ddd12..00000000 --- a/transport/internet/splithttp/mux.go +++ /dev/null @@ -1,113 +0,0 @@ -package splithttp - -import ( - "context" - "crypto/rand" - "math" - "math/big" - "sync/atomic" - "time" - - "github.com/xtls/xray-core/common/errors" -) - -type XmuxConn interface { - IsClosed() bool -} - -type XmuxClient struct { - XmuxConn XmuxConn - OpenUsage atomic.Int32 - leftUsage int32 - LeftRequests atomic.Int32 - UnreusableAt time.Time -} - -type XmuxManager struct { - xmuxConfig XmuxConfig - concurrency int32 - connections int32 - newConnFunc func() XmuxConn - xmuxClients []*XmuxClient -} - -func NewXmuxManager(xmuxConfig XmuxConfig, newConnFunc func() XmuxConn) *XmuxManager { - return &XmuxManager{ - xmuxConfig: xmuxConfig, - concurrency: xmuxConfig.GetNormalizedMaxConcurrency().rand(), - connections: xmuxConfig.GetNormalizedMaxConnections().rand(), - newConnFunc: newConnFunc, - xmuxClients: make([]*XmuxClient, 0), - } -} - -func (m *XmuxManager) newXmuxClient() *XmuxClient { - xmuxClient := &XmuxClient{ - XmuxConn: m.newConnFunc(), - leftUsage: -1, - } - if x := m.xmuxConfig.GetNormalizedCMaxReuseTimes().rand(); x > 0 { - xmuxClient.leftUsage = x - 1 - } - xmuxClient.LeftRequests.Store(math.MaxInt32) - if x := m.xmuxConfig.GetNormalizedHMaxRequestTimes().rand(); x > 0 { - xmuxClient.LeftRequests.Store(x) - } - if x := m.xmuxConfig.GetNormalizedHMaxReusableSecs().rand(); x > 0 { - xmuxClient.UnreusableAt = time.Now().Add(time.Duration(x) * time.Second) - } - m.xmuxClients = append(m.xmuxClients, xmuxClient) - return xmuxClient -} - -func (m *XmuxManager) GetXmuxClient(ctx context.Context) *XmuxClient { // when locking - for i := 0; i < len(m.xmuxClients); { - xmuxClient := m.xmuxClients[i] - if xmuxClient.XmuxConn.IsClosed() || - xmuxClient.leftUsage == 0 || - xmuxClient.LeftRequests.Load() <= 0 || - (xmuxClient.UnreusableAt != time.Time{} && time.Now().After(xmuxClient.UnreusableAt)) { - errors.LogDebug(ctx, "XMUX: removing xmuxClient, IsClosed() = ", xmuxClient.XmuxConn.IsClosed(), - ", OpenUsage = ", xmuxClient.OpenUsage.Load(), - ", leftUsage = ", xmuxClient.leftUsage, - ", LeftRequests = ", xmuxClient.LeftRequests.Load(), - ", UnreusableAt = ", xmuxClient.UnreusableAt) - m.xmuxClients = append(m.xmuxClients[:i], m.xmuxClients[i+1:]...) - } else { - i++ - } - } - - if len(m.xmuxClients) == 0 { - errors.LogDebug(ctx, "XMUX: creating xmuxClient because xmuxClients is empty") - return m.newXmuxClient() - } - - if m.connections > 0 && len(m.xmuxClients) < int(m.connections) { - errors.LogDebug(ctx, "XMUX: creating xmuxClient because maxConnections was not hit, xmuxClients = ", len(m.xmuxClients)) - return m.newXmuxClient() - } - - xmuxClients := make([]*XmuxClient, 0) - if m.concurrency > 0 { - for _, xmuxClient := range m.xmuxClients { - if xmuxClient.OpenUsage.Load() < m.concurrency { - xmuxClients = append(xmuxClients, xmuxClient) - } - } - } else { - xmuxClients = m.xmuxClients - } - - if len(xmuxClients) == 0 { - errors.LogDebug(ctx, "XMUX: creating xmuxClient because maxConcurrency was hit, xmuxClients = ", len(m.xmuxClients)) - return m.newXmuxClient() - } - - i, _ := rand.Int(rand.Reader, big.NewInt(int64(len(xmuxClients)))) - xmuxClient := xmuxClients[i.Int64()] - if xmuxClient.leftUsage > 0 { - xmuxClient.leftUsage -= 1 - } - return xmuxClient -} diff --git a/transport/internet/splithttp/mux_test.go b/transport/internet/splithttp/mux_test.go deleted file mode 100644 index 835d07f0..00000000 --- a/transport/internet/splithttp/mux_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package splithttp_test - -import ( - "context" - "testing" - - . "github.com/xtls/xray-core/transport/internet/splithttp" -) - -type fakeRoundTripper struct{} - -func (f *fakeRoundTripper) IsClosed() bool { - return false -} - -func TestMaxConnections(t *testing.T) { - xmuxConfig := XmuxConfig{ - MaxConnections: &RangeConfig{From: 4, To: 4}, - } - - xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn { - return &fakeRoundTripper{} - }) - - xmuxClients := make(map[interface{}]struct{}) - for i := 0; i < 8; i++ { - xmuxClients[xmuxManager.GetXmuxClient(context.Background())] = struct{}{} - } - - if len(xmuxClients) != 4 { - t.Error("did not get 4 distinct clients, got ", len(xmuxClients)) - } -} - -func TestCMaxReuseTimes(t *testing.T) { - xmuxConfig := XmuxConfig{ - CMaxReuseTimes: &RangeConfig{From: 2, To: 2}, - } - - xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn { - return &fakeRoundTripper{} - }) - - xmuxClients := make(map[interface{}]struct{}) - for i := 0; i < 64; i++ { - xmuxClients[xmuxManager.GetXmuxClient(context.Background())] = struct{}{} - } - - if len(xmuxClients) != 32 { - t.Error("did not get 32 distinct clients, got ", len(xmuxClients)) - } -} - -func TestMaxConcurrency(t *testing.T) { - xmuxConfig := XmuxConfig{ - MaxConcurrency: &RangeConfig{From: 2, To: 2}, - } - - xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn { - return &fakeRoundTripper{} - }) - - xmuxClients := make(map[interface{}]struct{}) - for i := 0; i < 64; i++ { - xmuxClient := xmuxManager.GetXmuxClient(context.Background()) - xmuxClient.OpenUsage.Add(1) - xmuxClients[xmuxClient] = struct{}{} - } - - if len(xmuxClients) != 32 { - t.Error("did not get 32 distinct clients, got ", len(xmuxClients)) - } -} - -func TestDefault(t *testing.T) { - xmuxConfig := XmuxConfig{} - - xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn { - return &fakeRoundTripper{} - }) - - xmuxClients := make(map[interface{}]struct{}) - for i := 0; i < 64; i++ { - xmuxClient := xmuxManager.GetXmuxClient(context.Background()) - xmuxClient.OpenUsage.Add(1) - xmuxClients[xmuxClient] = struct{}{} - } - - if len(xmuxClients) != 1 { - t.Error("did not get 1 distinct clients, got ", len(xmuxClients)) - } -} diff --git a/transport/internet/splithttp/splithttp.go b/transport/internet/splithttp/splithttp.go deleted file mode 100644 index 2076e933..00000000 --- a/transport/internet/splithttp/splithttp.go +++ /dev/null @@ -1,3 +0,0 @@ -package splithttp - -const protocolName = "splithttp" diff --git a/transport/internet/splithttp/splithttp_test.go b/transport/internet/splithttp/splithttp_test.go deleted file mode 100644 index 1db54255..00000000 --- a/transport/internet/splithttp/splithttp_test.go +++ /dev/null @@ -1,463 +0,0 @@ -package splithttp_test - -import ( - "context" - "crypto/rand" - "fmt" - "io" - "net/http" - "runtime" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol/tls/cert" - "github.com/xtls/xray-core/testing/servers/tcp" - "github.com/xtls/xray-core/testing/servers/udp" - "github.com/xtls/xray-core/transport/internet" - . "github.com/xtls/xray-core/transport/internet/splithttp" - "github.com/xtls/xray-core/transport/internet/stat" - "github.com/xtls/xray-core/transport/internet/tls" -) - -func Test_ListenXHAndDial(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Path: "/sh", - }, - }, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - - var b [1024]byte - c.SetReadDeadline(time.Now().Add(2 * time.Second)) - _, err := c.Read(b[:]) - if err != nil { - return - } - - common.Must2(c.Write([]byte("Response"))) - }(conn) - }) - common.Must(err) - ctx := context.Background() - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{Path: "sh"}, - } - conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - fmt.Println("test2") - n, _ := io.ReadFull(conn, b[:]) - fmt.Println("string is", n) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - - common.Must(conn.Close()) - conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 2")) - common.Must(err) - n, _ = io.ReadFull(conn, b[:]) - common.Must(err) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - common.Must(conn.Close()) - - common.Must(listen.Close()) -} - -func TestDialWithRemoteAddr(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Path: "sh", - }, - }, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - - var b [1024]byte - _, err := c.Read(b[:]) - // common.Must(err) - if err != nil { - return - } - - _, err = c.Write([]byte(c.RemoteAddr().String())) - common.Must(err) - }(conn) - }) - common.Must(err) - - conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{Path: "sh", Headers: map[string]string{"X-Forwarded-For": "1.1.1.1"}}, - }) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - n, _ := io.ReadFull(conn, b[:]) - if string(b[:n]) != "1.1.1.1:0" { - t.Error("response: ", string(b[:n])) - } - - common.Must(listen.Close()) -} - -func Test_ListenXHAndDial_TLS(t *testing.T) { - if runtime.GOARCH == "arm64" { - return - } - - listenPort := tcp.PickPort() - - start := time.Now() - - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Path: "shs", - }, - SecurityType: "tls", - SecuritySettings: &tls.Config{ - AllowInsecure: true, - Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))}, - }, - } - listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { - go func() { - defer conn.Close() - - var b [1024]byte - conn.SetReadDeadline(time.Now().Add(2 * time.Second)) - _, err := conn.Read(b[:]) - if err != nil { - return - } - - common.Must2(conn.Write([]byte("Response"))) - }() - }) - common.Must(err) - defer listen.Close() - - conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - common.Must(err) - - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - n, _ := io.ReadFull(conn, b[:]) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - - end := time.Now() - if !end.Before(start.Add(time.Second * 5)) { - t.Error("end: ", end, " start: ", start) - } -} - -func Test_ListenXHAndDial_H2C(t *testing.T) { - if runtime.GOARCH == "arm64" { - return - } - - listenPort := tcp.PickPort() - - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Path: "shs", - }, - } - listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { - go func() { - _ = conn.Close() - }() - }) - common.Must(err) - defer listen.Close() - - protocols := new(http.Protocols) - protocols.SetUnencryptedHTTP2(true) - client := http.Client{ - Transport: &http.Transport{ - Protocols: protocols, - }, - } - - resp, err := client.Get("http://" + net.LocalHostIP.String() + ":" + listenPort.String()) - common.Must(err) - - if resp.StatusCode != 404 { - t.Error("Expected 404 but got:", resp.StatusCode) - } - - if resp.ProtoMajor != 2 { - t.Error("Expected h2 but got:", resp.ProtoMajor) - } -} - -func Test_ListenXHAndDial_QUIC(t *testing.T) { - if runtime.GOARCH == "arm64" { - return - } - - listenPort := udp.PickPort() - - start := time.Now() - - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Path: "shs", - }, - SecurityType: "tls", - SecuritySettings: &tls.Config{ - AllowInsecure: true, - Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))}, - NextProtocol: []string{"h3"}, - }, - } - - serverClosed := false - listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { - go func() { - defer conn.Close() - - b := buf.New() - defer b.Release() - - for { - b.Clear() - if _, err := b.ReadFrom(conn); err != nil { - break - } - common.Must2(conn.Write(b.Bytes())) - } - - serverClosed = true - }() - }) - common.Must(err) - defer listen.Close() - - time.Sleep(time.Second) - - conn, err := Dial(context.Background(), net.UDPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - common.Must(err) - - const N = 1024 - b1 := make([]byte, N) - common.Must2(rand.Read(b1)) - b2 := buf.New() - - common.Must2(conn.Write(b1)) - - b2.Clear() - common.Must2(b2.ReadFullFrom(conn, N)) - if r := cmp.Diff(b2.Bytes(), b1); r != "" { - t.Error(r) - } - - common.Must2(conn.Write(b1)) - - b2.Clear() - common.Must2(b2.ReadFullFrom(conn, N)) - if r := cmp.Diff(b2.Bytes(), b1); r != "" { - t.Error(r) - } - - conn.Close() - time.Sleep(100 * time.Millisecond) - if !serverClosed { - t.Error("server did not get closed") - } - - end := time.Now() - if !end.Before(start.Add(time.Second * 5)) { - t.Error("end: ", end, " start: ", start) - } -} - -func Test_ListenXHAndDial_Unix(t *testing.T) { - tempDir := t.TempDir() - tempSocket := tempDir + "/server.sock" - - listen, err := ListenXH(context.Background(), net.DomainAddress(tempSocket), 0, &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Path: "/sh", - }, - }, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - - var b [1024]byte - c.SetReadDeadline(time.Now().Add(2 * time.Second)) - _, err := c.Read(b[:]) - if err != nil { - return - } - - common.Must2(c.Write([]byte("Response"))) - }(conn) - }) - common.Must(err) - ctx := context.Background() - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Host: "example.com", - Path: "sh", - }, - } - conn, err := Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)), streamSettings) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - fmt.Println("test2") - n, _ := io.ReadFull(conn, b[:]) - fmt.Println("string is", n) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - - common.Must(conn.Close()) - conn, err = Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)), streamSettings) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 2")) - common.Must(err) - n, _ = io.ReadFull(conn, b[:]) - common.Must(err) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - common.Must(conn.Close()) - - common.Must(listen.Close()) -} - -func Test_queryString(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - // this querystring does not have any effect, but sometimes people blindly copy it from websocket config. make sure the outbound doesn't break - Path: "/sh?ed=2048", - }, - }, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - - var b [1024]byte - c.SetReadDeadline(time.Now().Add(2 * time.Second)) - _, err := c.Read(b[:]) - if err != nil { - return - } - - common.Must2(c.Write([]byte("Response"))) - }(conn) - }) - common.Must(err) - ctx := context.Background() - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{Path: "sh?ed=2048"}, - } - conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - - common.Must(err) - _, err = conn.Write([]byte("Test connection 1")) - common.Must(err) - - var b [1024]byte - fmt.Println("test2") - n, _ := io.ReadFull(conn, b[:]) - fmt.Println("string is", n) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - - common.Must(conn.Close()) - common.Must(listen.Close()) -} - -func Test_maxUpload(t *testing.T) { - listenPort := tcp.PickPort() - streamSettings := &internet.MemoryStreamConfig{ - ProtocolName: "splithttp", - ProtocolSettings: &Config{ - Path: "/sh", - ScMaxEachPostBytes: &RangeConfig{ - From: 10000, - To: 10000, - }, - }, - } - - var uploadSize int - listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { - go func(c stat.Connection) { - defer c.Close() - var b [10240]byte - c.SetReadDeadline(time.Now().Add(2 * time.Second)) - n, err := c.Read(b[:]) - if err != nil { - return - } - - uploadSize = n - - common.Must2(c.Write([]byte("Response"))) - }(conn) - }) - common.Must(err) - ctx := context.Background() - - conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) - - // send a slightly too large upload - var upload [10001]byte - _, err = conn.Write(upload[:]) - common.Must(err) - - var b [10240]byte - n, _ := io.ReadFull(conn, b[:]) - fmt.Println("string is", n) - if string(b[:n]) != "Response" { - t.Error("response: ", string(b[:n])) - } - common.Must(conn.Close()) - - if uploadSize > 10000 || uploadSize == 0 { - t.Error("incorrect upload size: ", uploadSize) - } - - common.Must(listen.Close()) -} diff --git a/transport/internet/splithttp/upload_queue.go b/transport/internet/splithttp/upload_queue.go deleted file mode 100644 index 69b9a972..00000000 --- a/transport/internet/splithttp/upload_queue.go +++ /dev/null @@ -1,164 +0,0 @@ -package splithttp - -// upload_queue is a specialized priorityqueue + channel to reorder generic -// packets by a sequence number - -import ( - "container/heap" - "io" - "runtime" - "sync" - - "github.com/xtls/xray-core/common/errors" -) - -type Packet struct { - Reader io.ReadCloser - Payload []byte - Seq uint64 -} - -type uploadQueue struct { - reader io.ReadCloser - nomore bool - pushedPackets chan Packet - writeCloseMutex sync.Mutex - heap uploadHeap - nextSeq uint64 - closed bool - maxPackets int -} - -func NewUploadQueue(maxPackets int) *uploadQueue { - return &uploadQueue{ - pushedPackets: make(chan Packet, maxPackets), - heap: uploadHeap{}, - nextSeq: 0, - closed: false, - maxPackets: maxPackets, - } -} - -func (h *uploadQueue) Push(p Packet) error { - h.writeCloseMutex.Lock() - defer h.writeCloseMutex.Unlock() - - if h.closed { - return errors.New("packet queue closed") - } - if h.nomore { - return errors.New("h.reader already exists") - } - if p.Reader != nil { - h.nomore = true - } - h.pushedPackets <- p - return nil -} - -func (h *uploadQueue) Close() error { - h.writeCloseMutex.Lock() - defer h.writeCloseMutex.Unlock() - - if !h.closed { - h.closed = true - runtime.Gosched() // hope Read() gets the packet - f: - for { - select { - case p := <-h.pushedPackets: - if p.Reader != nil { - h.reader = p.Reader - } - default: - break f - } - } - close(h.pushedPackets) - } - if h.reader != nil { - return h.reader.Close() - } - return nil -} - -func (h *uploadQueue) Read(b []byte) (int, error) { - if h.reader != nil { - return h.reader.Read(b) - } - - if h.closed { - return 0, io.EOF - } - - if len(h.heap) == 0 { - packet, more := <-h.pushedPackets - if !more { - return 0, io.EOF - } - if packet.Reader != nil { - h.reader = packet.Reader - return h.reader.Read(b) - } - heap.Push(&h.heap, packet) - } - - for len(h.heap) > 0 { - packet := heap.Pop(&h.heap).(Packet) - n := 0 - - if packet.Seq == h.nextSeq { - copy(b, packet.Payload) - n = min(len(b), len(packet.Payload)) - - if n < len(packet.Payload) { - // partial read - packet.Payload = packet.Payload[n:] - heap.Push(&h.heap, packet) - } else { - h.nextSeq = packet.Seq + 1 - } - - return n, nil - } - - // misordered packet - if packet.Seq > h.nextSeq { - if len(h.heap) > h.maxPackets { - // the "reassembly buffer" is too large, and we want to - // constrain memory usage somehow. let's tear down the - // connection, and hope the application retries. - return 0, errors.New("packet queue is too large") - } - heap.Push(&h.heap, packet) - packet2, more := <-h.pushedPackets - if !more { - return 0, io.EOF - } - heap.Push(&h.heap, packet2) - } - } - - return 0, nil -} - -// heap code directly taken from https://pkg.go.dev/container/heap -type uploadHeap []Packet - -func (h uploadHeap) Len() int { return len(h) } -func (h uploadHeap) Less(i, j int) bool { return h[i].Seq < h[j].Seq } -func (h uploadHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } - -func (h *uploadHeap) Push(x any) { - // Push and Pop use pointer receivers because they modify the slice's length, - // not just its contents. - *h = append(*h, x.(Packet)) -} - -func (h *uploadHeap) Pop() any { - old := *h - n := len(old) - x := old[n-1] - *h = old[0 : n-1] - return x -} diff --git a/transport/internet/splithttp/upload_queue_test.go b/transport/internet/splithttp/upload_queue_test.go deleted file mode 100644 index 8185cd8f..00000000 --- a/transport/internet/splithttp/upload_queue_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package splithttp_test - -import ( - "testing" - - "github.com/xtls/xray-core/common" - . "github.com/xtls/xray-core/transport/internet/splithttp" -) - -func Test_regression_readzero(t *testing.T) { - q := NewUploadQueue(10) - q.Push(Packet{ - Payload: []byte("x"), - Seq: 0, - }) - buf := make([]byte, 20) - n, err := q.Read(buf) - common.Must(err) - if n != 1 { - t.Error("n=", n) - } -} diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index ba7db103..93cf404e 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -2,13 +2,11 @@ package internet import ( "context" - "math/rand" "syscall" "time" - "github.com/sagernet/sing/common/control" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/outbound" ) @@ -17,11 +15,10 @@ var effectiveSystemDialer SystemDialer = &DefaultSystemDialer{} type SystemDialer interface { Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *SocketConfig) (net.Conn, error) - DestIpAddress() net.IP } type DefaultSystemDialer struct { - controllers []control.Func + controllers []controller dns dns.Client obm outbound.Manager } @@ -49,7 +46,7 @@ func hasBindAddr(sockopt *SocketConfig) bool { } func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { - errors.LogDebug(ctx, "dialing to "+dest.String()) + newError("dialing to " + dest.String()).AtDebug().WriteToLog() if dest.Network == net.Network_UDP && !hasBindAddr(sockopt) { srcAddr := resolveSrcAddr(net.Network_UDP, src) @@ -59,26 +56,11 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne Port: 0, } } - var lc net.ListenConfig - destAddr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) + packetConn, err := ListenSystemPacket(ctx, srcAddr, sockopt) if err != nil { return nil, err } - lc.Control = func(network, address string, c syscall.RawConn) error { - for _, ctl := range d.controllers { - if err := ctl(network, address, c); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply external controller") - } - } - return c.Control(func(fd uintptr) { - if sockopt != nil { - if err := applyOutboundSocketOptions(network, destAddr.String(), fd, sockopt); err != nil { - errors.LogInfo(ctx, err, "failed to apply socket options") - } - } - }) - } - packetConn, err := lc.ListenPacket(ctx, srcAddr.Network(), srcAddr.String()) + destAddr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) if err != nil { return nil, err } @@ -98,26 +80,24 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne } if sockopt != nil || len(d.controllers) > 0 { - if sockopt != nil && sockopt.TcpMptcp { - dialer.SetMultipathTCP(true) - } dialer.Control = func(network, address string, c syscall.RawConn) error { - for _, ctl := range d.controllers { - if err := ctl(network, address, c); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply external controller") - } - } return c.Control(func(fd uintptr) { if sockopt != nil { if err := applyOutboundSocketOptions(network, address, fd, sockopt); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply socket options") + newError("failed to apply socket options").Base(err).WriteToLog(session.ExportIDToError(ctx)) } if dest.Network == net.Network_UDP && hasBindAddr(sockopt) { if err := bindAddr(fd, sockopt.BindAddress, sockopt.BindPort); err != nil { - errors.LogInfoInner(ctx, err, "failed to bind source address to ", sockopt.BindAddress) + newError("failed to bind source address to ", sockopt.BindAddress).Base(err).WriteToLog(session.ExportIDToError(ctx)) } } } + + for _, ctl := range d.controllers { + if err := ctl(network, address, fd); err != nil { + newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + } }) } } @@ -125,10 +105,6 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne return dialer.DialContext(ctx, dest.Network.SystemString(), dest.NetAddr()) } -func (d *DefaultSystemDialer) DestIpAddress() net.IP { - return nil -} - type PacketConnWrapper struct { Conn net.PacketConn Dest net.Addr @@ -193,10 +169,6 @@ func (v *SimpleSystemDialer) Dial(ctx context.Context, src net.Address, dest net return v.adapter.Dial(dest.Network.SystemString(), dest.NetAddr()) } -func (d *SimpleSystemDialer) DestIpAddress() net.IP { - return nil -} - // UseAlternativeSystemDialer replaces the current system dialer with a given one. // Caller must ensure there is no race condition. // @@ -213,42 +185,16 @@ func UseAlternativeSystemDialer(dialer SystemDialer) { // It only works when effective dialer is the default dialer. // // xray:api:beta -func RegisterDialerController(ctl control.Func) error { +func RegisterDialerController(ctl func(network, address string, fd uintptr) error) error { if ctl == nil { - return errors.New("nil listener controller") + return newError("nil listener controller") } dialer, ok := effectiveSystemDialer.(*DefaultSystemDialer) if !ok { - return errors.New("RegisterListenerController not supported in custom dialer") + return newError("RegisterListenerController not supported in custom dialer") } dialer.controllers = append(dialer.controllers, ctl) return nil } - -type FakePacketConn struct { - net.Conn -} - -func (c *FakePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - n, err = c.Read(p) - return n, c.RemoteAddr(), err -} - -func (c *FakePacketConn) WriteTo(p []byte, _ net.Addr) (n int, err error) { - return c.Write(p) -} - -func (c *FakePacketConn) LocalAddr() net.Addr { - return &net.TCPAddr{ - IP: net.IP{byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256))}, - Port: rand.Intn(65536), - } -} - -func (c *FakePacketConn) SetReadBuffer(bytes int) error { - // do nothing, this function is only there to suppress quic-go printing - // random warnings about UDP buffers to stdout - return nil -} diff --git a/transport/internet/system_listener.go b/transport/internet/system_listener.go index fa3092a1..04694383 100644 --- a/transport/internet/system_listener.go +++ b/transport/internet/system_listener.go @@ -10,97 +10,72 @@ import ( "time" "github.com/pires/go-proxyproto" - "github.com/sagernet/sing/common/control" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" ) var effectiveListener = DefaultListener{} +type controller func(network, address string, fd uintptr) error + type DefaultListener struct { - controllers []control.Func + controllers []controller } -func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []control.Func) func(network, address string, c syscall.RawConn) error { +func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []controller) func(network, address string, c syscall.RawConn) error { return func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { - for _, controller := range controllers { - if err := controller(network, address, c); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply external controller") - } - } - if sockopt != nil { if err := applyInboundSocketOptions(network, fd, sockopt); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply socket options to incoming connection") + newError("failed to apply socket options to incoming connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } setReusePort(fd) + + for _, controller := range controllers { + if err := controller(network, address, fd); err != nil { + newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + } }) } } -// For some reason, other component of ray will assume the listener is a TCP listener and have valid remote address. -// But in fact it doesn't. So we need to wrap the listener to make it return 0.0.0.0(unspecified) as remote address. -// If other issues encountered, we should able to fix it here. -type UnixListenerWrapper struct { - *net.UnixListener - locker *FileLocker -} - -func (l *UnixListenerWrapper) Accept() (net.Conn, error) { - conn, err := l.UnixListener.Accept() - if err != nil { - return nil, err - } - return &UnixConnWrapper{UnixConn: conn.(*net.UnixConn)}, nil -} - -func (l *UnixListenerWrapper) Close() error { - if l.locker != nil { - l.locker.Release() - l.locker = nil - } - return l.UnixListener.Close() -} - -type UnixConnWrapper struct { - *net.UnixConn -} - -func (conn *UnixConnWrapper) RemoteAddr() net.Addr { - return &net.TCPAddr{ - IP: []byte{0, 0, 0, 0}, - } -} - func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (l net.Listener, err error) { var lc net.ListenConfig var network, address string - // callback is called after the Listen function returns - callback := func(l net.Listener, err error) (net.Listener, error) { - return l, err - } switch addr := addr.(type) { case *net.TCPAddr: network = addr.Network() address = addr.String() lc.Control = getControlFunc(ctx, sockopt, dl.controllers) - if sockopt != nil { - if sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0 { - lc.KeepAlive = time.Duration(-1) - } - if sockopt.TcpMptcp { - lc.SetMultipathTCP(true) - } + if sockopt != nil && (sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0) { + lc.KeepAlive = time.Duration(-1) } case *net.UnixAddr: lc.Control = nil network = addr.Network() address = addr.Name + if s := strings.Split(address, ","); len(s) == 2 { + address = s[0] + perm, perr := strconv.ParseUint(s[1], 8, 32) + if perr != nil { + return nil, newError("failed to parse permission: " + s[1]).Base(perr) + } + + defer func(file string, permission os.FileMode) { + if err == nil { + cerr := os.Chmod(address, permission) + if cerr != nil { + err = newError("failed to set permission for " + file).Base(cerr) + } + } + }(address, os.FileMode(perm)) + } + if (runtime.GOOS == "linux" || runtime.GOOS == "android") && address[0] == '@' { // linux abstract unix domain socket is lockfree if len(address) > 1 && address[1] == '@' { @@ -110,48 +85,20 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S address = string(fullAddr) } } else { - // split permission from address - var filePerm *os.FileMode - if s := strings.Split(address, ","); len(s) == 2 { - address = s[0] - perm, perr := strconv.ParseUint(s[1], 8, 32) - if perr != nil { - return nil, errors.New("failed to parse permission: " + s[1]).Base(perr) - } - - mode := os.FileMode(perm) - filePerm = &mode - } // normal unix domain socket needs lock locker := &FileLocker{ path: address + ".lock", } - if err := locker.Acquire(); err != nil { + err := locker.Acquire() + if err != nil { return nil, err } - - // set callback to combine listener and set permission - callback = func(l net.Listener, err error) (net.Listener, error) { - if err != nil { - locker.Release() - return nil, err - } - l = &UnixListenerWrapper{UnixListener: l.(*net.UnixListener), locker: locker} - if filePerm == nil { - return l, nil - } - err = os.Chmod(address, *filePerm) - if err != nil { - l.Close() - return nil, errors.New("failed to set permission for " + address).Base(err) - } - return l, nil - } + ctx = context.WithValue(ctx, address, locker) } } - l, err = callback(lc.Listen(ctx, network, address)) - if err == nil && sockopt != nil && sockopt.AcceptProxyProtocol { + l, err = lc.Listen(ctx, network, address) + if sockopt != nil && sockopt.AcceptProxyProtocol { policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil } l = &proxyproto.Listener{Listener: l, Policy: policyFunc} } @@ -170,9 +117,9 @@ func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sock // The controller can be used to operate on file descriptors before they are put into use. // // xray:api:beta -func RegisterListenerController(controller control.Func) error { +func RegisterListenerController(controller func(network, address string, fd uintptr) error) error { if controller == nil { - return errors.New("nil listener controller") + return newError("nil listener controller") } effectiveListener.controllers = append(effectiveListener.controllers, controller) diff --git a/transport/internet/system_listener_test.go b/transport/internet/system_listener_test.go index 390888e7..0fcc9a95 100644 --- a/transport/internet/system_listener_test.go +++ b/transport/internet/system_listener_test.go @@ -3,10 +3,8 @@ package internet_test import ( "context" "net" - "syscall" "testing" - "github.com/sagernet/sing/common/control" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/transport/internet" ) @@ -14,11 +12,9 @@ import ( func TestRegisterListenerController(t *testing.T) { var gotFd uintptr - common.Must(internet.RegisterListenerController(func(network, address string, conn syscall.RawConn) error { - return control.Raw(conn, func(fd uintptr) error { - gotFd = fd - return nil - }) + common.Must(internet.RegisterListenerController(func(network string, addr string, fd uintptr) error { + gotFd = fd + return nil })) conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ diff --git a/transport/internet/tagged/tagged.go b/transport/internet/tagged/tagged.go index 2cd9dcd2..430f9640 100644 --- a/transport/internet/tagged/tagged.go +++ b/transport/internet/tagged/tagged.go @@ -4,9 +4,8 @@ import ( "context" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/features/routing" ) -type DialFunc func(ctx context.Context, dispatcher routing.Dispatcher, dest net.Destination, tag string) (net.Conn, error) +type DialFunc func(ctx context.Context, dest net.Destination, tag string) (net.Conn, error) var Dialer DialFunc diff --git a/transport/internet/tagged/taggedimpl/errors.generated.go b/transport/internet/tagged/taggedimpl/errors.generated.go new file mode 100644 index 00000000..178794cc --- /dev/null +++ b/transport/internet/tagged/taggedimpl/errors.generated.go @@ -0,0 +1,9 @@ +package taggedimpl + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/tagged/taggedimpl/impl.go b/transport/internet/tagged/taggedimpl/impl.go index 2a773401..6b66e11f 100644 --- a/transport/internet/tagged/taggedimpl/impl.go +++ b/transport/internet/tagged/taggedimpl/impl.go @@ -3,7 +3,6 @@ package taggedimpl import ( "context" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/session" @@ -12,10 +11,17 @@ import ( "github.com/xtls/xray-core/transport/internet/tagged" ) -func DialTaggedOutbound(ctx context.Context, dispatcher routing.Dispatcher, dest net.Destination, tag string) (net.Conn, error) { +func DialTaggedOutbound(ctx context.Context, dest net.Destination, tag string) (net.Conn, error) { + var dispatcher routing.Dispatcher if core.FromContext(ctx) == nil { - return nil, errors.New("Instance context variable is not in context, dial denied. ") + return nil, newError("Instance context variable is not in context, dial denied. ") } + if err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) { + dispatcher = dispatcherInstance + }); err != nil { + return nil, newError("Required Feature dispatcher not resolved").Base(err) + } + content := new(session.Content) content.SkipDNSResolve = true diff --git a/transport/internet/tagged/taggedimpl/taggedimpl.go b/transport/internet/tagged/taggedimpl/taggedimpl.go index 3116922e..ef2f3c02 100644 --- a/transport/internet/tagged/taggedimpl/taggedimpl.go +++ b/transport/internet/tagged/taggedimpl/taggedimpl.go @@ -1 +1,3 @@ package taggedimpl + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/tcp/config.go b/transport/internet/tcp/config.go index e8851d0e..f21b63f6 100644 --- a/transport/internet/tcp/config.go +++ b/transport/internet/tcp/config.go @@ -5,6 +5,8 @@ import ( "github.com/xtls/xray-core/transport/internet" ) +const protocolName = "tcp" + func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) diff --git a/transport/internet/tcp/config.pb.go b/transport/internet/tcp/config.pb.go index e4deb0e8..c2b949de 100644 --- a/transport/internet/tcp/config.pb.go +++ b/transport/internet/tcp/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/tcp/config.proto package tcp @@ -32,9 +32,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_tcp_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_tcp_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -45,7 +47,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tcp_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -116,7 +118,7 @@ func file_transport_internet_tcp_config_proto_rawDescGZIP() []byte { } var file_transport_internet_tcp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_tcp_config_proto_goTypes = []any{ +var file_transport_internet_tcp_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.transport.internet.tcp.Config (*serial.TypedMessage)(nil), // 1: xray.common.serial.TypedMessage } @@ -134,6 +136,20 @@ func file_transport_internet_tcp_config_proto_init() { if File_transport_internet_tcp_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_tcp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index 63b9d627..840062b1 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -2,11 +2,8 @@ package tcp import ( "context" - "slices" - "strings" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" @@ -15,76 +12,23 @@ import ( "github.com/xtls/xray-core/transport/internet/tls" ) -func IsFromMitm(str string) bool { - return strings.ToLower(str) == "frommitm" -} - // Dial dials a new TCP connection to the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { - errors.LogInfo(ctx, "dialing TCP to ", dest) + newError("dialing TCP to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) if err != nil { return nil, err } if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { - mitmServerName := session.MitmServerNameFromContext(ctx) - mitmAlpn11 := session.MitmAlpn11FromContext(ctx) tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) - if IsFromMitm(tlsConfig.ServerName) { - tlsConfig.ServerName = mitmServerName - } - isFromMitmVerify := false - if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertInNames) > 0 { - for i, name := range r.VerifyPeerCertInNames { - if IsFromMitm(name) { - isFromMitmVerify = true - r.VerifyPeerCertInNames[0], r.VerifyPeerCertInNames[i] = r.VerifyPeerCertInNames[i], r.VerifyPeerCertInNames[0] - r.VerifyPeerCertInNames = r.VerifyPeerCertInNames[1:] - after := mitmServerName - for { - if len(after) > 0 { - r.VerifyPeerCertInNames = append(r.VerifyPeerCertInNames, after) - } - _, after, _ = strings.Cut(after, ".") - if !strings.Contains(after, ".") { - break - } - } - slices.Reverse(r.VerifyPeerCertInNames) - break - } - } - } - isFromMitmAlpn := len(tlsConfig.NextProtos) == 1 && IsFromMitm(tlsConfig.NextProtos[0]) - if isFromMitmAlpn { - if mitmAlpn11 { - tlsConfig.NextProtos[0] = "http/1.1" - } else { - tlsConfig.NextProtos = []string{"h2", "http/1.1"} - } - } if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil { conn = tls.UClient(conn, tlsConfig, fingerprint) - if len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "http/1.1" { // allow manually specify - err = conn.(*tls.UConn).WebsocketHandshakeContext(ctx) - } else { - err = conn.(*tls.UConn).HandshakeContext(ctx) + if err := conn.(*tls.UConn).Handshake(); err != nil { + return nil, err } } else { conn = tls.Client(conn, tlsConfig) - err = conn.(*tls.Conn).HandshakeContext(ctx) - } - if err != nil { - if isFromMitmVerify { - return nil, errors.New("MITM freedom RAW TLS: failed to verify Domain Fronting certificate from " + mitmServerName).Base(err).AtWarning() - } - return nil, err - } - negotiatedProtocol := conn.(tls.Interface).NegotiatedProtocol() - if isFromMitmAlpn && !mitmAlpn11 && negotiatedProtocol != "h2" { - conn.Close() - return nil, errors.New("MITM freedom RAW TLS: unexpected Negotiated Protocol (" + negotiatedProtocol + ") with " + mitmServerName).AtWarning() } } else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil { if conn, err = reality.UClient(conn, config, ctx, dest); err != nil { @@ -96,11 +40,11 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me if tcpSettings.HeaderSettings != nil { headerConfig, err := tcpSettings.HeaderSettings.GetInstance() if err != nil { - return nil, errors.New("failed to get header settings").Base(err).AtError() + return nil, newError("failed to get header settings").Base(err).AtError() } auth, err := internet.CreateConnectionAuthenticator(headerConfig) if err != nil { - return nil, errors.New("failed to create header authenticator").Base(err).AtError() + return nil, newError("failed to create header authenticator").Base(err).AtError() } conn = auth.Client(conn) } diff --git a/transport/internet/tcp/errors.generated.go b/transport/internet/tcp/errors.generated.go new file mode 100644 index 00000000..0832d114 --- /dev/null +++ b/transport/internet/tcp/errors.generated.go @@ -0,0 +1,9 @@ +package tcp + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 8747fca2..392228c6 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -8,8 +8,8 @@ import ( goreality "github.com/xtls/reality" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" @@ -24,6 +24,7 @@ type Listener struct { authConfig internet.ConnectionAuthenticator config *Config addConn internet.ConnHandler + locker *internet.FileLocker // for unix domain socket } // ListenTCP creates a new Listener based on configurations. @@ -47,22 +48,26 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe Net: "unix", }, streamSettings.SocketSettings) if err != nil { - return nil, errors.New("failed to listen Unix Domain Socket on ", address).Base(err) + return nil, newError("failed to listen Unix Domain Socket on ", address).Base(err) + } + newError("listening Unix Domain Socket on ", address).WriteToLog(session.ExportIDToError(ctx)) + locker := ctx.Value(address.Domain()) + if locker != nil { + l.locker = locker.(*internet.FileLocker) } - errors.LogInfo(ctx, "listening Unix Domain Socket on ", address) } else { listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { - return nil, errors.New("failed to listen TCP on ", address, ":", port).Base(err) + return nil, newError("failed to listen TCP on ", address, ":", port).Base(err) } - errors.LogInfo(ctx, "listening TCP on ", address, ":", port) + newError("listening TCP on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx)) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { - errors.LogWarning(ctx, "accepting PROXY protocol") + newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } l.listener = listener @@ -72,17 +77,16 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe } if config := reality.ConfigFromStreamSettings(streamSettings); config != nil { l.realityConfig = config.GetREALITYConfig() - go goreality.DetectPostHandshakeRecordsLens(l.realityConfig) } if tcpSettings.HeaderSettings != nil { headerConfig, err := tcpSettings.HeaderSettings.GetInstance() if err != nil { - return nil, errors.New("invalid header settings").Base(err).AtError() + return nil, newError("invalid header settings").Base(err).AtError() } auth, err := internet.CreateConnectionAuthenticator(headerConfig) if err != nil { - return nil, errors.New("invalid header settings.").Base(err).AtError() + return nil, newError("invalid header settings.").Base(err).AtError() } l.authConfig = auth } @@ -99,7 +103,7 @@ func (v *Listener) keepAccepting() { if strings.Contains(errStr, "closed") { break } - errors.LogWarningInner(context.Background(), err, "failed to accepted raw connections") + newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog() if strings.Contains(errStr, "too many") { time.Sleep(time.Millisecond * 500) } @@ -110,7 +114,7 @@ func (v *Listener) keepAccepting() { conn = tls.Server(conn, v.tlsConfig) } else if v.realityConfig != nil { if conn, err = reality.Server(conn, v.realityConfig); err != nil { - errors.LogInfo(context.Background(), err.Error()) + newError(err).AtInfo().WriteToLog() return } } @@ -129,6 +133,9 @@ func (v *Listener) Addr() net.Addr { // Close implements internet.Listener.Close. func (v *Listener) Close() error { + if v.locker != nil { + v.locker.Release() + } return v.listener.Close() } diff --git a/transport/internet/tcp/sockopt_darwin.go b/transport/internet/tcp/sockopt_darwin.go index ec0da29a..a1905cf4 100644 --- a/transport/internet/tcp/sockopt_darwin.go +++ b/transport/internet/tcp/sockopt_darwin.go @@ -4,7 +4,6 @@ package tcp import ( - "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" @@ -16,11 +15,11 @@ func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { ra := conn.RemoteAddr() ip, port, err := internet.OriginalDst(la, ra) if err != nil { - return net.Destination{}, errors.New("failed to get destination").Base(err) + return net.Destination{}, newError("failed to get destination").Base(err) } dest := net.TCPDestination(net.IPAddress(ip), net.Port(port)) if !dest.IsValid() { - return net.Destination{}, errors.New("failed to parse destination.") + return net.Destination{}, newError("failed to parse destination.") } return dest, nil } diff --git a/transport/internet/tcp/sockopt_freebsd.go b/transport/internet/tcp/sockopt_freebsd.go index d7ab3ff0..906e06ea 100644 --- a/transport/internet/tcp/sockopt_freebsd.go +++ b/transport/internet/tcp/sockopt_freebsd.go @@ -4,7 +4,6 @@ package tcp import ( - "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" @@ -16,11 +15,11 @@ func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { ra := conn.RemoteAddr() ip, port, err := internet.OriginalDst(la, ra) if err != nil { - return net.Destination{}, errors.New("failed to get destination").Base(err) + return net.Destination{}, newError("failed to get destination").Base(err) } dest := net.TCPDestination(net.IPAddress(ip), net.Port(port)) if !dest.IsValid() { - return net.Destination{}, errors.New("failed to parse destination.") + return net.Destination{}, newError("failed to parse destination.") } return dest, nil } diff --git a/transport/internet/tcp/sockopt_linux.go b/transport/internet/tcp/sockopt_linux.go index f5648e8f..86044a80 100644 --- a/transport/internet/tcp/sockopt_linux.go +++ b/transport/internet/tcp/sockopt_linux.go @@ -4,11 +4,9 @@ package tcp import ( - "context" "syscall" "unsafe" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/transport/internet/stat" ) @@ -18,11 +16,11 @@ const SO_ORIGINAL_DST = 80 func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { sysrawconn, f := conn.(syscall.Conn) if !f { - return net.Destination{}, errors.New("unable to get syscall.Conn") + return net.Destination{}, newError("unable to get syscall.Conn") } rawConn, err := sysrawconn.SyscallConn() if err != nil { - return net.Destination{}, errors.New("failed to get sys fd").Base(err) + return net.Destination{}, newError("failed to get sys fd").Base(err) } var dest net.Destination err = rawConn.Control(func(fd uintptr) { @@ -32,7 +30,7 @@ func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { } addr, err := syscall.GetsockoptIPv6MTUInfo(int(fd), level, SO_ORIGINAL_DST) if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to call getsockopt") + newError("failed to call getsockopt").Base(err).WriteToLog() return } ip := (*[4]byte)(unsafe.Pointer(&addr.Addr.Flowinfo))[:4] @@ -43,10 +41,10 @@ func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { dest = net.TCPDestination(net.IPAddress(ip), net.PortFromBytes(port)) }) if err != nil { - return net.Destination{}, errors.New("failed to control connection").Base(err) + return net.Destination{}, newError("failed to control connection").Base(err) } if !dest.IsValid() { - return net.Destination{}, errors.New("failed to call getsockopt") + return net.Destination{}, newError("failed to call getsockopt") } return dest, nil } diff --git a/transport/internet/tcp/tcp.go b/transport/internet/tcp/tcp.go index 9a86457a..f9e66d84 100644 --- a/transport/internet/tcp/tcp.go +++ b/transport/internet/tcp/tcp.go @@ -1,3 +1,3 @@ package tcp -const protocolName = "tcp" +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/tcp_hub.go b/transport/internet/tcp_hub.go index cf0ad80d..2ebff9a7 100644 --- a/transport/internet/tcp_hub.go +++ b/transport/internet/tcp_hub.go @@ -3,7 +3,6 @@ package internet import ( "context" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/transport/internet/stat" ) @@ -12,7 +11,7 @@ var transportListenerCache = make(map[string]ListenFunc) func RegisterTransportListener(protocol string, listener ListenFunc) error { if _, found := transportListenerCache[protocol]; found { - return errors.New(protocol, " listener already registered.").AtError() + return newError(protocol, " listener already registered.").AtError() } transportListenerCache[protocol] = listener return nil @@ -32,7 +31,7 @@ func ListenUnix(ctx context.Context, address net.Address, settings *MemoryStream if settings == nil { s, err := ToMemoryStreamConfig(nil) if err != nil { - return nil, errors.New("failed to create default unix stream settings").Base(err) + return nil, newError("failed to create default unix stream settings").Base(err) } settings = s } @@ -40,11 +39,11 @@ func ListenUnix(ctx context.Context, address net.Address, settings *MemoryStream protocol := settings.ProtocolName listenFunc := transportListenerCache[protocol] if listenFunc == nil { - return nil, errors.New(protocol, " unix listener not registered.").AtError() + return nil, newError(protocol, " unix istener not registered.").AtError() } listener, err := listenFunc(ctx, address, net.Port(0), settings, handler) if err != nil { - return nil, errors.New("failed to listen on unix address: ", address).Base(err) + return nil, newError("failed to listen on unix address: ", address).Base(err) } return listener, nil } @@ -53,7 +52,7 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, settings if settings == nil { s, err := ToMemoryStreamConfig(nil) if err != nil { - return nil, errors.New("failed to create default stream settings").Base(err) + return nil, newError("failed to create default stream settings").Base(err) } settings = s } @@ -63,17 +62,17 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, settings } if address.Family().IsDomain() { - return nil, errors.New("domain address is not allowed for listening: ", address.Domain()) + return nil, newError("domain address is not allowed for listening: ", address.Domain()) } protocol := settings.ProtocolName listenFunc := transportListenerCache[protocol] if listenFunc == nil { - return nil, errors.New(protocol, " listener not registered.").AtError() + return nil, newError(protocol, " listener not registered.").AtError() } listener, err := listenFunc(ctx, address, port, settings, handler) if err != nil { - return nil, errors.New("failed to listen on address: ", address, ":", port).Base(err) + return nil, newError("failed to listen on address: ", address, ":", port).Base(err) } return listener, nil } diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index d6701a7d..9c1f8eee 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -1,20 +1,14 @@ package tls import ( - "bytes" - "context" "crypto/hmac" - "crypto/rand" "crypto/tls" "crypto/x509" "encoding/base64" - "os" - "slices" "strings" "sync" "time" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/ocsp" "github.com/xtls/xray-core/common/platform/filesystem" @@ -40,7 +34,7 @@ func (c *Config) loadSelfCertPool() (*x509.CertPool, error) { root := x509.NewCertPool() for _, cert := range c.Certificate { if !root.AppendCertsFromPEM(cert.Certificate) { - return nil, errors.New("failed to append cert").AtWarning() + return nil, newError("failed to append cert").AtWarning() } } return root, nil @@ -53,84 +47,72 @@ func (c *Config) BuildCertificates() []*tls.Certificate { if entry.Usage != Certificate_ENCIPHERMENT { continue } - getX509KeyPair := func() *tls.Certificate { - keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key) - if err != nil { - errors.LogWarningInner(context.Background(), err, "ignoring invalid X509 key pair") - return nil - } - keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) - if err != nil { - errors.LogWarningInner(context.Background(), err, "ignoring invalid certificate") - return nil - } - return &keyPair - } - if keyPair := getX509KeyPair(); keyPair != nil { - certs = append(certs, keyPair) - } else { + keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key) + if err != nil { + newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog() continue } - index := len(certs) - 1 - setupOcspTicker(entry, func(isReloaded, isOcspstapling bool) { - cert := certs[index] - if isReloaded { - if newKeyPair := getX509KeyPair(); newKeyPair != nil { - cert = newKeyPair - } else { - return - } + keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) + if err != nil { + newError("ignoring invalid certificate").Base(err).AtWarning().WriteToLog() + continue + } + certs = append(certs, &keyPair) + if !entry.OneTimeLoading { + var isOcspstapling bool + hotReloadCertInterval := uint64(3600) + if entry.OcspStapling != 0 { + hotReloadCertInterval = entry.OcspStapling + isOcspstapling = true } - if isOcspstapling { - if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil { - errors.LogWarningInner(context.Background(), err, "ignoring invalid OCSP") - } else if string(newOCSPData) != string(cert.OCSPStaple) { - cert.OCSPStaple = newOCSPData + index := len(certs) - 1 + go func(entry *Certificate, cert *tls.Certificate, index int) { + t := time.NewTicker(time.Duration(hotReloadCertInterval) * time.Second) + for { + if entry.CertificatePath != "" && entry.KeyPath != "" { + newCert, err := filesystem.ReadFile(entry.CertificatePath) + if err != nil { + newError("failed to parse certificate").Base(err).AtError().WriteToLog() + <-t.C + continue + } + newKey, err := filesystem.ReadFile(entry.KeyPath) + if err != nil { + newError("failed to parse key").Base(err).AtError().WriteToLog() + <-t.C + continue + } + if string(newCert) != string(entry.Certificate) && string(newKey) != string(entry.Key) { + newKeyPair, err := tls.X509KeyPair(newCert, newKey) + if err != nil { + newError("ignoring invalid X509 key pair").Base(err).AtError().WriteToLog() + <-t.C + continue + } + if newKeyPair.Leaf, err = x509.ParseCertificate(newKeyPair.Certificate[0]); err != nil { + newError("ignoring invalid certificate").Base(err).AtError().WriteToLog() + <-t.C + continue + } + cert = &newKeyPair + } + } + if isOcspstapling { + if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil { + newError("ignoring invalid OCSP").Base(err).AtWarning().WriteToLog() + } else if string(newOCSPData) != string(cert.OCSPStaple) { + cert.OCSPStaple = newOCSPData + } + } + certs[index] = cert + <-t.C } - } - certs[index] = cert - }) + }(entry, certs[index], index) + } } return certs } -func setupOcspTicker(entry *Certificate, callback func(isReloaded, isOcspstapling bool)) { - go func() { - if entry.OneTimeLoading { - return - } - var isOcspstapling bool - hotReloadCertInterval := uint64(3600) - if entry.OcspStapling != 0 { - hotReloadCertInterval = entry.OcspStapling - isOcspstapling = true - } - t := time.NewTicker(time.Duration(hotReloadCertInterval) * time.Second) - for { - var isReloaded bool - if entry.CertificatePath != "" && entry.KeyPath != "" { - newCert, err := filesystem.ReadCert(entry.CertificatePath) - if err != nil { - errors.LogErrorInner(context.Background(), err, "failed to parse certificate") - return - } - newKey, err := filesystem.ReadCert(entry.KeyPath) - if err != nil { - errors.LogErrorInner(context.Background(), err, "failed to parse key") - return - } - if string(newCert) != string(entry.Certificate) || string(newKey) != string(entry.Key) { - entry.Certificate = newCert - entry.Key = newKey - isReloaded = true - } - } - callback(isReloaded, isOcspstapling) - <-t.C - } - }() -} - func isCertificateExpired(c *tls.Certificate) bool { if c.Leaf == nil && len(c.Certificate) > 0 { if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil { @@ -145,16 +127,13 @@ func isCertificateExpired(c *tls.Certificate) bool { func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) { parent, err := cert.ParseCertificate(rawCA.Certificate, rawCA.Key) if err != nil { - return nil, errors.New("failed to parse raw certificate").Base(err) + return nil, newError("failed to parse raw certificate").Base(err) } newCert, err := cert.Generate(parent, cert.CommonName(domain), cert.DNSNames(domain)) if err != nil { - return nil, errors.New("failed to generate new certificate for ", domain).Base(err) + return nil, newError("failed to generate new certificate for ", domain).Base(err) } newCertPEM, newKeyPEM := newCert.ToPEM() - if rawCA.BuildChain { - newCertPEM = bytes.Join([][]byte{newCertPEM, rawCA.Certificate}, []byte("\n")) - } cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM) return &cert, err } @@ -164,7 +143,6 @@ func (c *Config) getCustomCA() []*Certificate { for _, certificate := range c.Certificate { if certificate.Usage == Certificate_AUTHORITY_ISSUE { certs = append(certs, certificate) - setupOcspTicker(certificate, func(isReloaded, isOcspstapling bool) {}) } } return certs @@ -197,7 +175,7 @@ func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.Cli newCerts = append(newCerts, certificate) } else if certificate.Leaf != nil { expTime := certificate.Leaf.NotAfter.Format(time.RFC3339) - errors.LogInfo(context.Background(), "old certificate for ", domain, " (expire on ", expTime, ") discarded") + newError("old certificate for ", domain, " (expire on ", expTime, ") discarded").AtInfo().WriteToLog() } } @@ -212,16 +190,16 @@ func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.Cli if rawCert.Usage == Certificate_AUTHORITY_ISSUE { newCert, err := issueCertificate(rawCert, domain) if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to issue new certificate for ", domain) + newError("failed to issue new certificate for ", domain).Base(err).WriteToLog() continue } parsed, err := x509.ParseCertificate(newCert.Certificate[0]) if err == nil { newCert.Leaf = parsed expTime := parsed.NotAfter.Format(time.RFC3339) - errors.LogInfo(context.Background(), "new certificate for ", domain, " (expire on ", expTime, ") issued") + newError("new certificate for ", domain, " (expire on ", expTime, ") issued").AtInfo().WriteToLog() } else { - errors.LogInfoInner(context.Background(), err, "failed to parse new certificate for ", domain) + newError("failed to parse new certificate for ", domain).Base(err).WriteToLog() } access.Lock() @@ -233,7 +211,7 @@ func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.Cli } if issuedCertificate == nil { - return nil, errors.New("failed to create a new certificate for ", domain) + return nil, newError("failed to create a new certificate for ", domain) } access.Lock() @@ -278,74 +256,38 @@ func (c *Config) parseServerName() string { return c.ServerName } -func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - if r.VerifyPeerCertInNames != nil { - if len(r.VerifyPeerCertInNames) > 0 { - certs := make([]*x509.Certificate, len(rawCerts)) - for i, asn1Data := range rawCerts { - certs[i], _ = x509.ParseCertificate(asn1Data) - } - opts := x509.VerifyOptions{ - Roots: r.RootCAs, - CurrentTime: time.Now(), - Intermediates: x509.NewCertPool(), - } - for _, cert := range certs[1:] { - opts.Intermediates.AddCert(cert) - } - for _, opts.DNSName = range r.VerifyPeerCertInNames { - if _, err := certs[0].Verify(opts); err == nil { - return nil - } - } - } - if r.PinnedPeerCertificateChainSha256 == nil { - return errors.New("peer cert is invalid.") - } - } - - if r.PinnedPeerCertificateChainSha256 != nil { +func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + if c.PinnedPeerCertificateChainSha256 != nil { hashValue := GenerateCertChainHash(rawCerts) - for _, v := range r.PinnedPeerCertificateChainSha256 { + for _, v := range c.PinnedPeerCertificateChainSha256 { if hmac.Equal(hashValue, v) { return nil } } - return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue)) + return newError("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue)) } - if r.PinnedPeerCertificatePublicKeySha256 != nil { + if c.PinnedPeerCertificatePublicKeySha256 != nil { for _, v := range verifiedChains { for _, cert := range v { publicHash := GenerateCertPublicKeyHash(cert) - for _, c := range r.PinnedPeerCertificatePublicKeySha256 { + for _, c := range c.PinnedPeerCertificatePublicKeySha256 { if hmac.Equal(publicHash, c) { return nil } } } } - return errors.New("peer public key is unrecognized.") + return newError("peer public key is unrecognized.") } return nil } -type RandCarrier struct { - RootCAs *x509.CertPool - VerifyPeerCertInNames []string - PinnedPeerCertificateChainSha256 [][]byte - PinnedPeerCertificatePublicKeySha256 [][]byte -} - -func (r *RandCarrier) Read(p []byte) (n int, err error) { - return rand.Read(p) -} - // GetTLSConfig converts this Config into tls.Config. func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { root, err := c.getCertPool() if err != nil { - errors.LogErrorInner(context.Background(), err, "failed to load system root certificate") + newError("failed to load system root certificate").AtError().Base(err).WriteToLog() } if c == nil { @@ -358,25 +300,13 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { } } - randCarrier := &RandCarrier{ - RootCAs: root, - VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames), - PinnedPeerCertificateChainSha256: c.PinnedPeerCertificateChainSha256, - PinnedPeerCertificatePublicKeySha256: c.PinnedPeerCertificatePublicKeySha256, - } config := &tls.Config{ - Rand: randCarrier, ClientSessionCache: globalSessionCache, RootCAs: root, InsecureSkipVerify: c.AllowInsecure, - NextProtos: slices.Clone(c.NextProtocol), + NextProtos: c.NextProtocol, SessionTicketsDisabled: !c.EnableSessionResumption, - VerifyPeerCertificate: randCarrier.verifyPeerCert, - } - if len(c.VerifyPeerCertInNames) > 0 { - config.InsecureSkipVerify = true - } else { - randCarrier.VerifyPeerCertInNames = nil + VerifyPeerCertificate: c.verifyPeerCert, } for _, opt := range opts { @@ -394,10 +324,6 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { config.ServerName = sn } - if len(c.CurvePreferences) > 0 { - config.CurvePreferences = ParseCurveName(c.CurvePreferences) - } - if len(config.NextProtos) == 0 { config.NextProtos = []string{"h2", "http/1.1"} } @@ -436,14 +362,7 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { } } - if len(c.MasterKeyLog) > 0 && c.MasterKeyLog != "none" { - writer, err := os.OpenFile(c.MasterKeyLog, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) - if err != nil { - errors.LogErrorInner(context.Background(), err, "failed to open ", c.MasterKeyLog, " as master key log") - } else { - config.KeyLogWriter = writer - } - } + config.PreferServerCipherSuites = c.PreferServerCipherSuites return config } @@ -452,13 +371,10 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { type Option func(*tls.Config) // WithDestination sets the server name in TLS config. -// Due to the incorrect structure of GetTLSConfig(), the config.ServerName will always be empty. -// So the real logic for SNI is: -// set it to dest -> overwrite it with servername(if it's len>0). func WithDestination(dest net.Destination) Option { return func(config *tls.Config) { - if config.ServerName == "" { - config.ServerName = dest.Address.String() + if dest.Address.Family().IsDomain() && config.ServerName == "" { + config.ServerName = dest.Address.Domain() } } } @@ -483,23 +399,3 @@ func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config { } return config } - -func ParseCurveName(curveNames []string) []tls.CurveID { - curveMap := map[string]tls.CurveID{ - "curvep256": tls.CurveP256, - "curvep384": tls.CurveP384, - "curvep521": tls.CurveP521, - "x25519": tls.X25519, - "x25519kyber768draft00": 0x6399, - } - - var curveIDs []tls.CurveID - for _, name := range curveNames { - if curveID, ok := curveMap[strings.ToLower(name)]; ok { - curveIDs = append(curveIDs, curveID) - } else { - errors.LogWarning(context.Background(), "unsupported curve name: "+name) - } - } - return curveIDs -} diff --git a/transport/internet/tls/config.pb.go b/transport/internet/tls/config.pb.go index bc45dc4e..d02fa112 100644 --- a/transport/internet/tls/config.pb.go +++ b/transport/internet/tls/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/tls/config.proto package tls @@ -86,14 +86,15 @@ type Certificate struct { KeyPath string `protobuf:"bytes,6,opt,name=key_path,json=keyPath,proto3" json:"key_path,omitempty"` // If true, one-Time Loading OneTimeLoading bool `protobuf:"varint,7,opt,name=One_time_loading,json=OneTimeLoading,proto3" json:"One_time_loading,omitempty"` - BuildChain bool `protobuf:"varint,8,opt,name=build_chain,json=buildChain,proto3" json:"build_chain,omitempty"` } func (x *Certificate) Reset() { *x = Certificate{} - mi := &file_transport_internet_tls_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_tls_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Certificate) String() string { @@ -104,7 +105,7 @@ func (*Certificate) ProtoMessage() {} func (x *Certificate) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tls_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -168,13 +169,6 @@ func (x *Certificate) GetOneTimeLoading() bool { return false } -func (x *Certificate) GetBuildChain() bool { - if x != nil { - return x.BuildChain - } - return false -} - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -199,31 +193,30 @@ type Config struct { MaxVersion string `protobuf:"bytes,8,opt,name=max_version,json=maxVersion,proto3" json:"max_version,omitempty"` // Specify cipher suites, except for TLS 1.3. CipherSuites string `protobuf:"bytes,9,opt,name=cipher_suites,json=cipherSuites,proto3" json:"cipher_suites,omitempty"` + // Whether the server selects its most preferred ciphersuite. + PreferServerCipherSuites bool `protobuf:"varint,10,opt,name=prefer_server_cipher_suites,json=preferServerCipherSuites,proto3" json:"prefer_server_cipher_suites,omitempty"` // TLS Client Hello fingerprint (uTLS). Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"` RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"` - // @Document Some certificate chain sha256 hashes. - // @Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted. + // @Document A pinned certificate chain sha256 hash. + // @Document If the server's hash does not match this value, the connection will be aborted. + // @Document This value replace allow_insecure. // @Critical PinnedPeerCertificateChainSha256 [][]byte `protobuf:"bytes,13,rep,name=pinned_peer_certificate_chain_sha256,json=pinnedPeerCertificateChainSha256,proto3" json:"pinned_peer_certificate_chain_sha256,omitempty"` - // @Document Some certificate public key sha256 hashes. - // @Document After normal validation (required), if one of certs in verified chain matches one of these values, the connection will be eventually accepted. + // @Document A pinned certificate public key sha256 hash. + // @Document If the server's public key hash does not match this value, the connection will be aborted. + // @Document This value replace allow_insecure. // @Critical PinnedPeerCertificatePublicKeySha256 [][]byte `protobuf:"bytes,14,rep,name=pinned_peer_certificate_public_key_sha256,json=pinnedPeerCertificatePublicKeySha256,proto3" json:"pinned_peer_certificate_public_key_sha256,omitempty"` - MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"` - // Lists of string as CurvePreferences values. - CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"` - // @Document Replaces server_name to verify the peer cert. - // @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried. - // @Critical - VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_tls_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_tls_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -234,7 +227,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tls_config_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -312,6 +305,13 @@ func (x *Config) GetCipherSuites() string { return "" } +func (x *Config) GetPreferServerCipherSuites() bool { + if x != nil { + return x.PreferServerCipherSuites + } + return false +} + func (x *Config) GetFingerprint() string { if x != nil { return x.Fingerprint @@ -340,27 +340,6 @@ func (x *Config) GetPinnedPeerCertificatePublicKeySha256() [][]byte { return nil } -func (x *Config) GetMasterKeyLog() string { - if x != nil { - return x.MasterKeyLog - } - return "" -} - -func (x *Config) GetCurvePreferences() []string { - if x != nil { - return x.CurvePreferences - } - return nil -} - -func (x *Config) GetVerifyPeerCertInNames() []string { - if x != nil { - return x.VerifyPeerCertInNames - } - return nil -} - var File_transport_internet_tls_config_proto protoreflect.FileDescriptor var file_transport_internet_tls_config_proto_rawDesc = []byte{ @@ -368,7 +347,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, - 0x6c, 0x73, 0x22, 0x83, 0x03, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x6c, 0x73, 0x22, 0xe2, 0x02, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -386,71 +365,64 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{ 0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45, - 0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, - 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, - 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, - 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x9a, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, - 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x28, 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, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, - 0x6e, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, - 0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x17, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, - 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, - 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, - 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b, 0x6e, - 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, - 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69, 0x12, - 0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x20, 0x70, - 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, - 0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12, 0x2b, - 0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76, 0x65, - 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x76, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, - 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, - 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 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, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, - 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x67, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x4e, + 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, + 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, + 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, + 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xcc, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65, + 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, + 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, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6e, + 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, + 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x69, + 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, + 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x3d, + 0x0a, 0x1b, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x20, 0x0a, + 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, + 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, + 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x6a, + 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69, 0x12, 0x4e, 0x0a, + 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x73, + 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x20, 0x70, 0x69, 0x6e, + 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x57, 0x0a, + 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, + 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0c, + 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 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, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, + 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -467,7 +439,7 @@ func file_transport_internet_tls_config_proto_rawDescGZIP() []byte { var file_transport_internet_tls_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_transport_internet_tls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_transport_internet_tls_config_proto_goTypes = []any{ +var file_transport_internet_tls_config_proto_goTypes = []interface{}{ (Certificate_Usage)(0), // 0: xray.transport.internet.tls.Certificate.Usage (*Certificate)(nil), // 1: xray.transport.internet.tls.Certificate (*Config)(nil), // 2: xray.transport.internet.tls.Config @@ -487,6 +459,32 @@ func file_transport_internet_tls_config_proto_init() { if File_transport_internet_tls_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_tls_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Certificate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_tls_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/tls/config.proto b/transport/internet/tls/config.proto index 3fac25af..227840a2 100644 --- a/transport/internet/tls/config.proto +++ b/transport/internet/tls/config.proto @@ -31,8 +31,6 @@ message Certificate { // If true, one-Time Loading bool One_time_loading = 7; - - bool build_chain = 8; } message Config { @@ -64,31 +62,25 @@ message Config { // Specify cipher suites, except for TLS 1.3. string cipher_suites = 9; + // Whether the server selects its most preferred ciphersuite. + bool prefer_server_cipher_suites = 10; + // TLS Client Hello fingerprint (uTLS). string fingerprint = 11; bool reject_unknown_sni = 12; - /* @Document Some certificate chain sha256 hashes. - @Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted. + /* @Document A pinned certificate chain sha256 hash. + @Document If the server's hash does not match this value, the connection will be aborted. + @Document This value replace allow_insecure. @Critical */ repeated bytes pinned_peer_certificate_chain_sha256 = 13; - /* @Document Some certificate public key sha256 hashes. - @Document After normal validation (required), if one of certs in verified chain matches one of these values, the connection will be eventually accepted. + /* @Document A pinned certificate public key sha256 hash. + @Document If the server's public key hash does not match this value, the connection will be aborted. + @Document This value replace allow_insecure. @Critical */ repeated bytes pinned_peer_certificate_public_key_sha256 = 14; - - string master_key_log = 15; - - // Lists of string as CurvePreferences values. - repeated string curve_preferences = 16; - - /* @Document Replaces server_name to verify the peer cert. - @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried. - @Critical - */ - repeated string verify_peer_cert_in_names = 17; } diff --git a/transport/internet/tls/config_other.go b/transport/internet/tls/config_other.go index efd18c93..59e0c153 100644 --- a/transport/internet/tls/config_other.go +++ b/transport/internet/tls/config_other.go @@ -6,8 +6,6 @@ package tls import ( "crypto/x509" "sync" - - "github.com/xtls/xray-core/common/errors" ) type rootCertsCache struct { @@ -44,12 +42,12 @@ func (c *Config) getCertPool() (*x509.CertPool, error) { pool, err := x509.SystemCertPool() if err != nil { - return nil, errors.New("system root").AtWarning().Base(err) + return nil, newError("system root").AtWarning().Base(err) } for _, cert := range c.Certificate { if !pool.AppendCertsFromPEM(cert.Certificate) { - return nil, errors.New("append cert to root").AtWarning().Base(err) + return nil, newError("append cert to root").AtWarning().Base(err) } } - return pool, nil + return pool, err } diff --git a/transport/internet/tls/errors.generated.go b/transport/internet/tls/errors.generated.go new file mode 100644 index 00000000..ba1851c8 --- /dev/null +++ b/transport/internet/tls/errors.generated.go @@ -0,0 +1,9 @@ +package tls + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/tls/grpc.go b/transport/internet/tls/grpc.go index 6e5dc578..a698196b 100644 --- a/transport/internet/tls/grpc.go +++ b/transport/internet/tls/grpc.go @@ -65,7 +65,7 @@ func (c *grpcUtls) ClientHandshake(ctx context.Context, authority string, rawCon conn := UClient(rawConn, cfg, c.fingerprint).(*UConn) errChannel := make(chan error, 1) go func() { - errChannel <- conn.HandshakeContext(ctx) + errChannel <- conn.Handshake() close(errChannel) }() select { diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index 28c7bf63..2fd9a017 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -1,42 +1,30 @@ package tls import ( - "context" "crypto/rand" "crypto/tls" "math/big" - "time" utls "github.com/refraction-networking/utls" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" ) +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + type Interface interface { net.Conn - HandshakeContext(ctx context.Context) error + Handshake() error VerifyHostname(host string) error - HandshakeContextServerName(ctx context.Context) string - NegotiatedProtocol() string + NegotiatedProtocol() (name string, mutual bool) } var _ buf.Writer = (*Conn)(nil) -var _ Interface = (*Conn)(nil) type Conn struct { *tls.Conn } -const tlsCloseTimeout = 250 * time.Millisecond - -func (c *Conn) Close() error { - timer := time.AfterFunc(tlsCloseTimeout, func() { - c.Conn.NetConn().Close() - }) - defer timer.Stop() - return c.Conn.Close() -} - func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error { mb = buf.Compact(mb) mb, err := buf.WriteMultiBuffer(c, mb) @@ -44,16 +32,20 @@ func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error { return err } -func (c *Conn) HandshakeContextServerName(ctx context.Context) string { - if err := c.HandshakeContext(ctx); err != nil { - return "" +func (c *Conn) HandshakeAddress() net.Address { + if err := c.Handshake(); err != nil { + return nil } - return c.ConnectionState().ServerName + state := c.ConnectionState() + if state.ServerName == "" { + return nil + } + return net.ParseAddress(state.ServerName) } -func (c *Conn) NegotiatedProtocol() string { +func (c *Conn) NegotiatedProtocol() (name string, mutual bool) { state := c.ConnectionState() - return state.NegotiatedProtocol + return state.NegotiatedProtocol, state.NegotiatedProtocolIsMutual } // Client initiates a TLS client handshake on the given connection. @@ -72,26 +64,20 @@ type UConn struct { *utls.UConn } -var _ Interface = (*UConn)(nil) - -func (c *UConn) Close() error { - timer := time.AfterFunc(tlsCloseTimeout, func() { - c.Conn.NetConn().Close() - }) - defer timer.Stop() - return c.Conn.Close() -} - -func (c *UConn) HandshakeContextServerName(ctx context.Context) string { - if err := c.HandshakeContext(ctx); err != nil { - return "" +func (c *UConn) HandshakeAddress() net.Address { + if err := c.Handshake(); err != nil { + return nil } - return c.ConnectionState().ServerName + state := c.ConnectionState() + if state.ServerName == "" { + return nil + } + return net.ParseAddress(state.ServerName) } // WebsocketHandshake basically calls UConn.Handshake inside it but it will only send // http/1.1 in its ALPN. -func (c *UConn) WebsocketHandshakeContext(ctx context.Context) error { +func (c *UConn) WebsocketHandshake() error { // Build the handshake state. This will apply every variable of the TLS of the // fingerprint in the UConn if err := c.BuildHandshakeState(); err != nil { @@ -113,12 +99,12 @@ func (c *UConn) WebsocketHandshakeContext(ctx context.Context) error { if err := c.BuildHandshakeState(); err != nil { return err } - return c.HandshakeContext(ctx) + return c.Handshake() } -func (c *UConn) NegotiatedProtocol() string { +func (c *UConn) NegotiatedProtocol() (name string, mutual bool) { state := c.ConnectionState() - return state.NegotiatedProtocol + return state.NegotiatedProtocol, state.NegotiatedProtocolIsMutual } func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) net.Conn { @@ -128,12 +114,10 @@ func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) ne func copyConfig(c *tls.Config) *utls.Config { return &utls.Config{ - Rand: c.Rand, RootCAs: c.RootCAs, ServerName: c.ServerName, InsecureSkipVerify: c.InsecureSkipVerify, VerifyPeerCertificate: c.VerifyPeerCertificate, - KeyLogWriter: c.KeyLogWriter, } } @@ -151,19 +135,15 @@ func init() { weights := utls.DefaultWeights weights.TLSVersMax_Set_VersionTLS13 = 1 weights.FirstKeyShare_Set_CurveP256 = 0 - randomized := utls.HelloRandomizedALPN + randomized := utls.HelloRandomized randomized.Seed, _ = utls.NewPRNGSeed() randomized.Weights = &weights - randomizednoalpn := utls.HelloRandomizedNoALPN - randomizednoalpn.Seed, _ = utls.NewPRNGSeed() - randomizednoalpn.Weights = &weights PresetFingerprints["randomized"] = &randomized - PresetFingerprints["randomizednoalpn"] = &randomizednoalpn } func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) { if name == "" { - return &utls.HelloChrome_Auto + return } if fingerprint = PresetFingerprints[name]; fingerprint != nil { return @@ -179,18 +159,16 @@ func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) { var PresetFingerprints = map[string]*utls.ClientHelloID{ // Recommended preset options in GUI clients - "chrome": &utls.HelloChrome_Auto, - "firefox": &utls.HelloFirefox_Auto, - "safari": &utls.HelloSafari_Auto, - "ios": &utls.HelloIOS_Auto, - "android": &utls.HelloAndroid_11_OkHttp, - "edge": &utls.HelloEdge_Auto, - "360": &utls.Hello360_Auto, - "qq": &utls.HelloQQ_Auto, - "random": nil, - "randomized": nil, - "randomizednoalpn": nil, - "unsafe": nil, + "chrome": &utls.HelloChrome_Auto, + "firefox": &utls.HelloFirefox_Auto, + "safari": &utls.HelloSafari_Auto, + "ios": &utls.HelloIOS_Auto, + "android": &utls.HelloAndroid_11_OkHttp, + "edge": &utls.HelloEdge_Auto, + "360": &utls.Hello360_Auto, + "qq": &utls.HelloQQ_Auto, + "random": nil, + "randomized": nil, } var ModernFingerprints = map[string]*utls.ClientHelloID{ @@ -198,15 +176,12 @@ var ModernFingerprints = map[string]*utls.ClientHelloID{ "hellofirefox_99": &utls.HelloFirefox_99, "hellofirefox_102": &utls.HelloFirefox_102, "hellofirefox_105": &utls.HelloFirefox_105, - "hellofirefox_120": &utls.HelloFirefox_120, "hellochrome_83": &utls.HelloChrome_83, "hellochrome_87": &utls.HelloChrome_87, "hellochrome_96": &utls.HelloChrome_96, "hellochrome_100": &utls.HelloChrome_100, "hellochrome_102": &utls.HelloChrome_102, "hellochrome_106_shuffle": &utls.HelloChrome_106_Shuffle, - "hellochrome_120": &utls.HelloChrome_120, - "hellochrome_131": &utls.HelloChrome_131, "helloios_13": &utls.HelloIOS_13, "helloios_14": &utls.HelloIOS_14, "helloedge_85": &utls.HelloEdge_85, @@ -241,12 +216,4 @@ var OtherFingerprints = map[string]*utls.ClientHelloID{ "hello360_auto": &utls.Hello360_Auto, "hello360_7_5": &utls.Hello360_7_5, "helloqq_auto": &utls.HelloQQ_Auto, - - // Chrome betas' - "hellochrome_100_psk": &utls.HelloChrome_100_PSK, - "hellochrome_112_psk_shuf": &utls.HelloChrome_112_PSK_Shuf, - "hellochrome_114_padding_psk_shuf": &utls.HelloChrome_114_Padding_PSK_Shuf, - "hellochrome_115_pq": &utls.HelloChrome_115_PQ, - "hellochrome_115_pq_psk": &utls.HelloChrome_115_PQ_PSK, - "hellochrome_120_pq": &utls.HelloChrome_120_PQ, } diff --git a/transport/internet/udp/config.pb.go b/transport/internet/udp/config.pb.go index d9d11a4f..c01b0e60 100644 --- a/transport/internet/udp/config.pb.go +++ b/transport/internet/udp/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/udp/config.proto package udp @@ -28,9 +28,11 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_udp_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_udp_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -41,7 +43,7 @@ func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_udp_config_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -87,7 +89,7 @@ func file_transport_internet_udp_config_proto_rawDescGZIP() []byte { } var file_transport_internet_udp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_udp_config_proto_goTypes = []any{ +var file_transport_internet_udp_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: xray.transport.internet.udp.Config } var file_transport_internet_udp_config_proto_depIdxs = []int32{ @@ -103,6 +105,20 @@ func file_transport_internet_udp_config_proto_init() { if File_transport_internet_udp_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_udp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/udp/dispatcher.go b/transport/internet/udp/dispatcher.go index e6267d2f..48b90b63 100644 --- a/transport/internet/udp/dispatcher.go +++ b/transport/internet/udp/dispatcher.go @@ -2,16 +2,16 @@ package udp import ( "context" - goerrors "errors" + "errors" "io" "sync" "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol/udp" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/features/routing" @@ -28,7 +28,7 @@ type connEntry struct { type Dispatcher struct { sync.RWMutex - conn *connEntry + conns map[net.Destination]*connEntry dispatcher routing.Dispatcher callback ResponseCallback callClose func() error @@ -36,18 +36,19 @@ type Dispatcher struct { func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Dispatcher { return &Dispatcher{ + conns: make(map[net.Destination]*connEntry), dispatcher: dispatcher, callback: callback, } } -func (v *Dispatcher) RemoveRay() { +func (v *Dispatcher) RemoveRay(dest net.Destination) { v.Lock() defer v.Unlock() - if v.conn != nil { - common.Interrupt(v.conn.link.Reader) - common.Close(v.conn.link.Writer) - v.conn = nil + if conn, found := v.conns[dest]; found { + common.Close(conn.link.Reader) + common.Close(conn.link.Writer) + delete(v.conns, dest) } } @@ -55,22 +56,22 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (* v.Lock() defer v.Unlock() - if v.conn != nil { - return v.conn, nil + if entry, found := v.conns[dest]; found { + return entry, nil } - errors.LogInfo(ctx, "establishing new connection for ", dest) + newError("establishing new connection for ", dest).WriteToLog() ctx, cancel := context.WithCancel(ctx) removeRay := func() { cancel() - v.RemoveRay() + v.RemoveRay(dest) } timer := signal.CancelAfterInactivity(ctx, removeRay, time.Minute) link, err := v.dispatcher.Dispatch(ctx, dest) if err != nil { - return nil, errors.New("failed to dispatch request to ", dest).Base(err) + return nil, newError("failed to dispatch request to ", dest).Base(err) } entry := &connEntry{ @@ -78,24 +79,24 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (* timer: timer, cancel: removeRay, } - v.conn = entry + v.conns[dest] = entry go handleInput(ctx, entry, dest, v.callback, v.callClose) return entry, nil } func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer) { // TODO: Add user to destString - errors.LogDebug(ctx, "dispatch request to: ", destination) + newError("dispatch request to: ", destination).AtDebug().WriteToLog(session.ExportIDToError(ctx)) conn, err := v.getInboundRay(ctx, destination) if err != nil { - errors.LogInfoInner(ctx, err, "failed to get inbound") + newError("failed to get inbound").Base(err).WriteToLog(session.ExportIDToError(ctx)) return } outputStream := conn.link.Writer if outputStream != nil { if err := outputStream.WriteMultiBuffer(buf.MultiBuffer{payload}); err != nil { - errors.LogInfoInner(ctx, err, "failed to write first UDP payload") + newError("failed to write first UDP payload").Base(err).WriteToLog(session.ExportIDToError(ctx)) conn.cancel() return } @@ -122,16 +123,13 @@ func handleInput(ctx context.Context, conn *connEntry, dest net.Destination, cal mb, err := input.ReadMultiBuffer() if err != nil { - if !goerrors.Is(err, io.EOF) { - errors.LogInfoInner(ctx, err, "failed to handle UDP input") + if !errors.Is(err, io.EOF) { + newError("failed to handle UDP input").Base(err).WriteToLog(session.ExportIDToError(ctx)) } return } timer.Update() for _, b := range mb { - if b.UDP != nil { - dest = *b.UDP - } callback(ctx, &udp.Packet{ Payload: b, Source: dest, @@ -144,17 +142,16 @@ type dispatcherConn struct { dispatcher *Dispatcher cache chan *udp.Packet done *done.Instance - ctx context.Context } func DialDispatcher(ctx context.Context, dispatcher routing.Dispatcher) (net.PacketConn, error) { c := &dispatcherConn{ cache: make(chan *udp.Packet, 16), done: done.New(), - ctx: ctx, } d := &Dispatcher{ + conns: make(map[net.Destination]*connEntry), dispatcher: dispatcher, callback: c.callback, callClose: c.Close, @@ -200,9 +197,8 @@ func (c *dispatcherConn) WriteTo(p []byte, addr net.Addr) (int, error) { n := copy(raw, p) buffer.Resize(0, int32(n)) - destination := net.DestinationFromAddr(addr) - buffer.UDP = &destination - c.dispatcher.Dispatch(c.ctx, destination, buffer) + ctx := context.Background() + c.dispatcher.Dispatch(ctx, net.DestinationFromAddr(addr), buffer) return n, nil } diff --git a/transport/internet/udp/errors.generated.go b/transport/internet/udp/errors.generated.go new file mode 100644 index 00000000..b1b4aa70 --- /dev/null +++ b/transport/internet/udp/errors.generated.go @@ -0,0 +1,9 @@ +package udp + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index c16085d5..d91f5536 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -4,7 +4,6 @@ import ( "context" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol/udp" "github.com/xtls/xray-core/transport/internet" @@ -55,7 +54,7 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe if err != nil { return nil, err } - errors.LogInfo(ctx, "listening UDP on ", address, ":", port) + newError("listening UDP on ", address, ":", port).WriteToLog() hub.conn = udpConn.(*net.UDPConn) hub.cache = make(chan *udp.Packet, hub.capacity) @@ -90,7 +89,7 @@ func (h *Hub) start() { n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes) if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to read UDP msg") + newError("failed to read UDP msg").Base(err).WriteToLog() buffer.Release() break } @@ -108,9 +107,9 @@ func (h *Hub) start() { if h.recvOrigDest && noob > 0 { payload.Target = RetrieveOriginalDest(oobBytes[:noob]) if payload.Target.IsValid() { - errors.LogDebug(context.Background(), "UDP original destination: ", payload.Target) + newError("UDP original destination: ", payload.Target).AtDebug().WriteToLog() } else { - errors.LogInfo(context.Background(), "failed to read UDP original destination") + newError("failed to read UDP original destination").WriteToLog() } } diff --git a/transport/internet/udp/udp.go b/transport/internet/udp/udp.go index 154bcc7e..d01148b2 100644 --- a/transport/internet/udp/udp.go +++ b/transport/internet/udp/udp.go @@ -1,3 +1,5 @@ package udp +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + const protocolName = "udp" diff --git a/transport/internet/websocket/config.go b/transport/internet/websocket/config.go index 4f2c0a14..948d32c4 100644 --- a/transport/internet/websocket/config.go +++ b/transport/internet/websocket/config.go @@ -7,6 +7,8 @@ import ( "github.com/xtls/xray-core/transport/internet" ) +const protocolName = "websocket" + func (c *Config) GetNormalizedPath() string { path := c.Path if path == "" { @@ -20,8 +22,8 @@ func (c *Config) GetNormalizedPath() string { func (c *Config) GetRequestHeader() http.Header { header := http.Header{} - for k, v := range c.Header { - header.Add(k, v) + for _, h := range c.Header { + header.Add(h.Key, h.Value) } return header } diff --git a/transport/internet/websocket/config.pb.go b/transport/internet/websocket/config.pb.go index ff6a3868..9ba5c1c8 100644 --- a/transport/internet/websocket/config.pb.go +++ b/transport/internet/websocket/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: transport/internet/websocket/config.proto package websocket @@ -20,24 +20,80 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type Header struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Header) Reset() { + *x = Header{} + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_websocket_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Header) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Header) ProtoMessage() {} + +func (x *Header) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_websocket_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Header.ProtoReflect.Descriptor instead. +func (*Header) Descriptor() ([]byte, []int) { + return file_transport_internet_websocket_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Header) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Header) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` // URL path to the WebSocket service. Empty value means root(/). - Header map[string]string `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - AcceptProxyProtocol bool `protobuf:"varint,4,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` - Ed uint32 `protobuf:"varint,5,opt,name=ed,proto3" json:"ed,omitempty"` - HeartbeatPeriod uint32 `protobuf:"varint,6,opt,name=heartbeatPeriod,proto3" json:"heartbeatPeriod,omitempty"` + // URL path to the WebSocket service. Empty value means root(/). + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Header []*Header `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty"` + AcceptProxyProtocol bool `protobuf:"varint,4,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` + Ed uint32 `protobuf:"varint,5,opt,name=ed,proto3" json:"ed,omitempty"` } func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_websocket_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_websocket_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Config) String() string { @@ -47,8 +103,8 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_websocket_config_proto_msgTypes[0] - if x != nil { + mi := &file_transport_internet_websocket_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -60,14 +116,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_transport_internet_websocket_config_proto_rawDescGZIP(), []int{0} -} - -func (x *Config) GetHost() string { - if x != nil { - return x.Host - } - return "" + return file_transport_internet_websocket_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetPath() string { @@ -77,7 +126,7 @@ func (x *Config) GetPath() string { return "" } -func (x *Config) GetHeader() map[string]string { +func (x *Config) GetHeader() []*Header { if x != nil { return x.Header } @@ -98,13 +147,6 @@ func (x *Config) GetEd() uint32 { return 0 } -func (x *Config) GetHeartbeatPeriod() uint32 { - if x != nil { - return x.HeartbeatPeriod - } - return 0 -} - var File_transport_internet_websocket_config_proto protoreflect.FileDescriptor var file_transport_internet_websocket_config_proto_rawDesc = []byte{ @@ -112,35 +154,30 @@ var file_transport_internet_websocket_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0xa8, - 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, - 0x68, 0x12, 0x4d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x35, 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, 0x77, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x02, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, - 0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x68, - 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x1a, 0x39, - 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x85, 0x01, 0x0a, 0x25, 0x63, 0x6f, - 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, + 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x30, + 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x22, 0xa9, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, + 0x41, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x29, 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, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x36, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0xaa, 0x02, 0x21, - 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x70, 0x72, 0x6f, + 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x02, 0x65, 0x64, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x42, 0x85, 0x01, 0x0a, + 0x25, 0x63, 0x6f, 0x6d, 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, 0x77, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x36, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -156,12 +193,12 @@ func file_transport_internet_websocket_config_proto_rawDescGZIP() []byte { } var file_transport_internet_websocket_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_transport_internet_websocket_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.transport.internet.websocket.Config - nil, // 1: xray.transport.internet.websocket.Config.HeaderEntry +var file_transport_internet_websocket_config_proto_goTypes = []interface{}{ + (*Header)(nil), // 0: xray.transport.internet.websocket.Header + (*Config)(nil), // 1: xray.transport.internet.websocket.Config } var file_transport_internet_websocket_config_proto_depIdxs = []int32{ - 1, // 0: xray.transport.internet.websocket.Config.header:type_name -> xray.transport.internet.websocket.Config.HeaderEntry + 0, // 0: xray.transport.internet.websocket.Config.header:type_name -> xray.transport.internet.websocket.Header 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 @@ -174,6 +211,32 @@ func file_transport_internet_websocket_config_proto_init() { if File_transport_internet_websocket_config_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_transport_internet_websocket_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Header); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_transport_internet_websocket_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/transport/internet/websocket/config.proto b/transport/internet/websocket/config.proto index 0ae73075..85365c33 100644 --- a/transport/internet/websocket/config.proto +++ b/transport/internet/websocket/config.proto @@ -6,11 +6,20 @@ option go_package = "github.com/xtls/xray-core/transport/internet/websocket"; option java_package = "com.xray.transport.internet.websocket"; option java_multiple_files = true; -message Config { - string host = 1; - string path = 2; // URL path to the WebSocket service. Empty value means root(/). - map header = 3; - bool accept_proxy_protocol = 4; - uint32 ed = 5; - uint32 heartbeatPeriod = 6; +message Header { + string key = 1; + string value = 2; +} + +message Config { + reserved 1; + + // URL path to the WebSocket service. Empty value means root(/). + string path = 2; + + repeated Header header = 3; + + bool accept_proxy_protocol = 4; + + uint32 ed = 5; } diff --git a/transport/internet/websocket/connection.go b/transport/internet/websocket/connection.go index 26082fc6..ca53b619 100644 --- a/transport/internet/websocket/connection.go +++ b/transport/internet/websocket/connection.go @@ -14,26 +14,13 @@ import ( var _ buf.Writer = (*connection)(nil) // connection is a wrapper for net.Conn over WebSocket connection. -// remoteAddr is used to pass "virtual" remote IP addresses in X-Forwarded-For. -// so we shouldn't directly read it form conn. type connection struct { conn *websocket.Conn reader io.Reader remoteAddr net.Addr } -func NewConnection(conn *websocket.Conn, remoteAddr net.Addr, extraReader io.Reader, heartbeatPeriod uint32) *connection { - if heartbeatPeriod != 0 { - go func() { - for { - time.Sleep(time.Duration(heartbeatPeriod) * time.Second) - if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Time{}); err != nil { - break - } - } - }() - } - +func newConnection(conn *websocket.Conn, remoteAddr net.Addr, extraReader io.Reader) *connection { return &connection{ conn: conn, remoteAddr: remoteAddr, @@ -87,15 +74,15 @@ func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { } func (c *connection) Close() error { - var errs []interface{} + var errors []interface{} if err := c.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(time.Second*5)); err != nil { - errs = append(errs, err) + errors = append(errors, err) } if err := c.conn.Close(); err != nil { - errs = append(errs, err) + errors = append(errors, err) } - if len(errs) > 0 { - return errors.New("failed to close connection").Base(errors.New(serial.Concat(errs...))) + if len(errors) > 0 { + return newError("failed to close connection").Base(newError(serial.Concat(errors...))) } return nil } diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 60330fd7..5017cb50 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -4,23 +4,47 @@ import ( "context" _ "embed" "encoding/base64" + "fmt" "io" gonet "net" + "net/http" + "os" "time" "github.com/gorilla/websocket" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/browser_dialer" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" ) +//go:embed dialer.html +var webpage []byte + +var conns chan *websocket.Conn + +func init() { + if addr := os.Getenv("XRAY_BROWSER_DIALER"); addr != "" { + conns = make(chan *websocket.Conn, 256) + go http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/websocket" { + if conn, err := upgrader.Upgrade(w, r, nil); err == nil { + conns <- conn + } else { + fmt.Println("unexpected error") + } + } else { + w.Write(webpage) + } + })) + } +} + // Dial dials a WebSocket connection to the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { - errors.LogInfo(ctx, "creating connection to ", dest) + newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) var conn net.Conn if streamSettings.ProtocolSettings.(*Config).Ed > 0 { ctx, cancel := context.WithCancel(ctx) @@ -34,7 +58,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me } else { var err error if conn, err = dialWebSocket(ctx, dest, streamSettings, nil); err != nil { - return nil, errors.New("failed to dial WebSocket").Base(err) + return nil, newError("failed to dial WebSocket").Base(err) } } return stat.Connection(conn), nil @@ -58,28 +82,27 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in protocol := "ws" - tConfig := tls.ConfigFromStreamSettings(streamSettings) - if tConfig != nil { + if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { protocol = "wss" - tlsConfig := tConfig.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) + tlsConfig := config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) dialer.TLSClientConfig = tlsConfig - if fingerprint := tls.GetFingerprint(tConfig.Fingerprint); fingerprint != nil { + if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil { dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (gonet.Conn, error) { // Like the NetDial in the dialer pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) if err != nil { - errors.LogErrorInner(ctx, err, "failed to dial to "+addr) + newError("failed to dial to " + addr).Base(err).AtError().WriteToLog() return nil, err } // TLS and apply the handshake cn := tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn) - if err := cn.WebsocketHandshakeContext(ctx); err != nil { - errors.LogErrorInner(ctx, err, "failed to dial to "+addr) + if err := cn.WebsocketHandshake(); err != nil { + newError("failed to dial to " + addr).Base(err).AtError().WriteToLog() return nil, err } if !tlsConfig.InsecureSkipVerify { if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil { - errors.LogErrorInner(ctx, err, "failed to dial to "+addr) + newError("failed to dial to " + addr).Base(err).AtError().WriteToLog() return nil, err } } @@ -94,39 +117,46 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in } uri := protocol + "://" + host + wsSettings.GetNormalizedPath() - if browser_dialer.HasBrowserDialer() { - conn, err := browser_dialer.DialWS(uri, ed) - if err != nil { - return nil, err + if conns != nil { + data := []byte(uri) + if ed != nil { + data = append(data, " "+base64.RawURLEncoding.EncodeToString(ed)...) } - - return NewConnection(conn, conn.RemoteAddr(), nil, wsSettings.HeartbeatPeriod), nil + var conn *websocket.Conn + for { + conn = <-conns + if conn.WriteMessage(websocket.TextMessage, data) != nil { + conn.Close() + } else { + break + } + } + if _, p, err := conn.ReadMessage(); err != nil { + conn.Close() + return nil, err + } else if s := string(p); s != "ok" { + conn.Close() + return nil, newError(s) + } + return newConnection(conn, conn.RemoteAddr(), nil), nil } header := wsSettings.GetRequestHeader() - // See dialer.DialContext() - header.Set("Host", wsSettings.Host) - if header.Get("Host") == "" && tConfig != nil { - header.Set("Host", tConfig.ServerName) - } - if header.Get("Host") == "" { - header.Set("Host", dest.Address.String()) - } if ed != nil { // RawURLEncoding is support by both V2Ray/V2Fly and XRay. header.Set("Sec-WebSocket-Protocol", base64.RawURLEncoding.EncodeToString(ed)) } - conn, resp, err := dialer.DialContext(ctx, uri, header) + conn, resp, err := dialer.Dial(uri, header) if err != nil { var reason string if resp != nil { reason = resp.Status } - return nil, errors.New("failed to dial to (", uri, "): ", reason).Base(err) + return nil, newError("failed to dial to (", uri, "): ", reason).Base(err) } - return NewConnection(conn, conn.RemoteAddr(), nil, wsSettings.HeartbeatPeriod), nil + return newConnection(conn, conn.RemoteAddr(), nil), nil } type delayDialConn struct { @@ -151,7 +181,7 @@ func (d *delayDialConn) Write(b []byte) (int, error) { var err error if d.Conn, err = dialWebSocket(d.ctx, d.dest, d.streamSettings, ed); err != nil { d.Close() - return 0, errors.New("failed to dial WebSocket").Base(err) + return 0, newError("failed to dial WebSocket").Base(err) } d.dialed <- true if ed != nil { diff --git a/transport/internet/websocket/dialer.html b/transport/internet/websocket/dialer.html new file mode 100644 index 00000000..c141379d --- /dev/null +++ b/transport/internet/websocket/dialer.html @@ -0,0 +1,55 @@ + + + + Browser Dialer + + + + + diff --git a/transport/internet/websocket/errors.generated.go b/transport/internet/websocket/errors.generated.go new file mode 100644 index 00000000..2361d0a6 --- /dev/null +++ b/transport/internet/websocket/errors.generated.go @@ -0,0 +1,9 @@ +package websocket + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index feefe2af..c0cf3446 100644 --- a/transport/internet/websocket/hub.go +++ b/transport/internet/websocket/hub.go @@ -13,15 +13,14 @@ import ( "github.com/gorilla/websocket" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" http_proto "github.com/xtls/xray-core/common/protocol/http" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" v2tls "github.com/xtls/xray-core/transport/internet/tls" ) type requestHandler struct { - host string path string ln *Listener } @@ -29,8 +28,8 @@ type requestHandler struct { var replacer = strings.NewReplacer("+", "-", "/", "_", "=", "") var upgrader = &websocket.Upgrader{ - ReadBufferSize: 0, - WriteBufferSize: 0, + ReadBufferSize: 4 * 1024, + WriteBufferSize: 4 * 1024, HandshakeTimeout: time.Second * 4, CheckOrigin: func(r *http.Request) bool { return true @@ -38,13 +37,7 @@ var upgrader = &websocket.Upgrader{ } func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { - if len(h.host) > 0 && !internet.IsValidHTTPHost(request.Host, h.host) { - errors.LogInfo(context.Background(), "failed to validate host, request:", request.Host, ", config:", h.host) - writer.WriteHeader(http.StatusNotFound) - return - } if request.URL.Path != h.path { - errors.LogInfo(context.Background(), "failed to validate path, request:", request.URL.Path, ", config:", h.path) writer.WriteHeader(http.StatusNotFound) return } @@ -60,7 +53,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req conn, err := upgrader.Upgrade(writer, request, responseHeader) if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to convert to WebSocket connection") + newError("failed to convert to WebSocket connection").Base(err).WriteToLog() return } @@ -73,7 +66,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req } } - h.ln.addConn(NewConnection(conn, remoteAddr, extraReader, h.ln.config.HeartbeatPeriod)) + h.ln.addConn(newConnection(conn, remoteAddr, extraReader)) } type Listener struct { @@ -82,6 +75,7 @@ type Listener struct { listener net.Listener config *Config addConn internet.ConnHandler + locker *internet.FileLocker // for unix domain socket } func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { @@ -104,22 +98,26 @@ func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSet Net: "unix", }, streamSettings.SocketSettings) if err != nil { - return nil, errors.New("failed to listen unix domain socket(for WS) on ", address).Base(err) + return nil, newError("failed to listen unix domain socket(for WS) on ", address).Base(err) + } + newError("listening unix domain socket(for WS) on ", address).WriteToLog(session.ExportIDToError(ctx)) + locker := ctx.Value(address.Domain()) + if locker != nil { + l.locker = locker.(*internet.FileLocker) } - errors.LogInfo(ctx, "listening unix domain socket(for WS) on ", address) } else { // tcp listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { - return nil, errors.New("failed to listen TCP(for WS) on ", address, ":", port).Base(err) + return nil, newError("failed to listen TCP(for WS) on ", address, ":", port).Base(err) } - errors.LogInfo(ctx, "listening TCP(for WS) on ", address, ":", port) + newError("listening TCP(for WS) on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx)) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { - errors.LogWarning(ctx, "accepting PROXY protocol") + newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { @@ -132,17 +130,16 @@ func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSet l.server = http.Server{ Handler: &requestHandler{ - host: wsSettings.Host, path: wsSettings.GetNormalizedPath(), ln: l, }, ReadHeaderTimeout: time.Second * 4, - MaxHeaderBytes: 8192, + MaxHeaderBytes: 4096, } go func() { if err := l.server.Serve(l.listener); err != nil { - errors.LogWarningInner(ctx, err, "failed to serve http for WebSocket") + newError("failed to serve http for WebSocket").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } }() @@ -156,6 +153,9 @@ func (ln *Listener) Addr() net.Addr { // Close implements net.Listener.Close(). func (ln *Listener) Close() error { + if ln.locker != nil { + ln.locker.Release() + } return ln.listener.Close() } diff --git a/transport/internet/websocket/ws.go b/transport/internet/websocket/ws.go index f05ded37..85c5ffb0 100644 --- a/transport/internet/websocket/ws.go +++ b/transport/internet/websocket/ws.go @@ -5,4 +5,4 @@ WebSocket transport implements an HTTP(S) compliable, surveillance proof transpo */ package websocket -const protocolName = "websocket" +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen diff --git a/transport/internet/websocket/ws_test.go b/transport/internet/websocket/ws_test.go index a9a2b885..f3ae38fb 100644 --- a/transport/internet/websocket/ws_test.go +++ b/transport/internet/websocket/ws_test.go @@ -9,7 +9,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol/tls/cert" - "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -17,8 +16,7 @@ import ( ) func Test_listenWSAndDial(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenWS(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ + listen, err := ListenWS(context.Background(), net.LocalHostIP, 13146, &internet.MemoryStreamConfig{ ProtocolName: "websocket", ProtocolSettings: &Config{ Path: "ws", @@ -28,7 +26,6 @@ func Test_listenWSAndDial(t *testing.T) { defer c.Close() var b [1024]byte - c.SetReadDeadline(time.Now().Add(2 * time.Second)) _, err := c.Read(b[:]) if err != nil { return @@ -44,7 +41,7 @@ func Test_listenWSAndDial(t *testing.T) { ProtocolName: "websocket", ProtocolSettings: &Config{Path: "ws"}, } - conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) + conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146), streamSettings) common.Must(err) _, err = conn.Write([]byte("Test connection 1")) @@ -58,7 +55,8 @@ func Test_listenWSAndDial(t *testing.T) { } common.Must(conn.Close()) - conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) + <-time.After(time.Second * 5) + conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146), streamSettings) common.Must(err) _, err = conn.Write([]byte("Test connection 2")) common.Must(err) @@ -73,8 +71,7 @@ func Test_listenWSAndDial(t *testing.T) { } func TestDialWithRemoteAddr(t *testing.T) { - listenPort := tcp.PickPort() - listen, err := ListenWS(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{ + listen, err := ListenWS(context.Background(), net.LocalHostIP, 13148, &internet.MemoryStreamConfig{ ProtocolName: "websocket", ProtocolSettings: &Config{ Path: "ws", @@ -90,15 +87,15 @@ func TestDialWithRemoteAddr(t *testing.T) { return } - _, err = c.Write([]byte(c.RemoteAddr().String())) + _, err = c.Write([]byte("Response")) common.Must(err) }(conn) }) common.Must(err) - conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), &internet.MemoryStreamConfig{ + conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), 13148), &internet.MemoryStreamConfig{ ProtocolName: "websocket", - ProtocolSettings: &Config{Path: "ws", Header: map[string]string{"X-Forwarded-For": "1.1.1.1"}}, + ProtocolSettings: &Config{Path: "ws", Header: []*Header{{Key: "X-Forwarded-For", Value: "1.1.1.1"}}}, }) common.Must(err) @@ -108,7 +105,7 @@ func TestDialWithRemoteAddr(t *testing.T) { var b [1024]byte n, err := conn.Read(b[:]) common.Must(err) - if string(b[:n]) != "1.1.1.1:0" { + if string(b[:n]) != "Response" { t.Error("response: ", string(b[:n])) } @@ -116,7 +113,6 @@ func TestDialWithRemoteAddr(t *testing.T) { } func Test_listenWSAndDial_TLS(t *testing.T) { - listenPort := tcp.PickPort() if runtime.GOARCH == "arm64" { return } @@ -134,7 +130,7 @@ func Test_listenWSAndDial_TLS(t *testing.T) { Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))}, }, } - listen, err := ListenWS(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { + listen, err := ListenWS(context.Background(), net.LocalHostIP, 13143, streamSettings, func(conn stat.Connection) { go func() { _ = conn.Close() }() @@ -142,7 +138,7 @@ func Test_listenWSAndDial_TLS(t *testing.T) { common.Must(err) defer listen.Close() - conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) + conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), 13143), streamSettings) common.Must(err) _ = conn.Close() diff --git a/transport/pipe/impl.go b/transport/pipe/impl.go index 4a30dbbb..14a18e63 100644 --- a/transport/pipe/impl.go +++ b/transport/pipe/impl.go @@ -24,6 +24,7 @@ const ( type pipeOption struct { limit int32 // maximum buffer size in bytes discardOverflow bool + onTransmission func(buffer buf.MultiBuffer) buf.MultiBuffer } func (o *pipeOption) isFull(curSize int32) bool { @@ -36,7 +37,6 @@ type pipe struct { readSignal *signal.Notifier writeSignal *signal.Notifier done *done.Instance - errChan chan error option pipeOption state state } @@ -46,14 +46,6 @@ var ( errSlowDown = errors.New("slow down") ) -func (p *pipe) Len() int32 { - data := p.data - if data == nil { - return 0 - } - return data.Len() -} - func (p *pipe) getState(forRead bool) error { switch p.state { case open: @@ -100,8 +92,6 @@ func (p *pipe) ReadMultiBuffer() (buf.MultiBuffer, error) { select { case <-p.readSignal.Wait(): case <-p.done.Wait(): - case err = <-p.errChan: - return nil, err } } } @@ -148,6 +138,10 @@ func (p *pipe) WriteMultiBuffer(mb buf.MultiBuffer) error { return nil } + if p.option.onTransmission != nil { + mb = p.option.onTransmission(mb) + } + for { err := p.writeMultiBufferInternal(mb) if err == nil { diff --git a/transport/pipe/pipe.go b/transport/pipe/pipe.go index f4b78303..0b22c2db 100644 --- a/transport/pipe/pipe.go +++ b/transport/pipe/pipe.go @@ -3,6 +3,7 @@ package pipe import ( "context" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/features/policy" @@ -25,6 +26,12 @@ func WithSizeLimit(limit int32) Option { } } +func OnTransmission(hook func(mb buf.MultiBuffer) buf.MultiBuffer) Option { + return func(option *pipeOption) { + option.onTransmission = hook + } +} + // DiscardOverflow returns an Option for Pipe to discard writes if full. func DiscardOverflow() Option { return func(opt *pipeOption) { @@ -52,7 +59,6 @@ func New(opts ...Option) (*Reader, *Writer) { readSignal: signal.NewNotifier(), writeSignal: signal.NewNotifier(), done: done.New(), - errChan: make(chan error, 1), option: pipeOption{ limit: -1, }, diff --git a/transport/pipe/reader.go b/transport/pipe/reader.go index 79f0ac03..66733436 100644 --- a/transport/pipe/reader.go +++ b/transport/pipe/reader.go @@ -25,17 +25,3 @@ func (r *Reader) ReadMultiBufferTimeout(d time.Duration) (buf.MultiBuffer, error func (r *Reader) Interrupt() { r.pipe.Interrupt() } - -// ReturnAnError makes ReadMultiBuffer return an error, only once. -func (r *Reader) ReturnAnError(err error) { - r.pipe.errChan <- err -} - -// Recover catches an error set by ReturnAnError, if exists. -func (r *Reader) Recover() (err error) { - select { - case err = <-r.pipe.errChan: - default: - } - return -} diff --git a/transport/pipe/writer.go b/transport/pipe/writer.go index 4ba26ccc..8230ec76 100644 --- a/transport/pipe/writer.go +++ b/transport/pipe/writer.go @@ -19,10 +19,6 @@ func (w *Writer) Close() error { return w.pipe.Close() } -func (w *Writer) Len() int32 { - return w.pipe.Len() -} - // Interrupt implements common.Interruptible. func (w *Writer) Interrupt() { w.pipe.Interrupt()