Compare commits

..

48 Commits

Author SHA1 Message Date
hmol233
1ced7985d5 Refine PrioritizedDomain, should fix https://github.com/XTLS/Xray-core/issues/638 2021-07-11 22:02:55 +08:00
hmol233
b85eef0131 Merge pull request #544 from AkinoKaede/dns-geo
Merge branch 'dns' into dns-geo
2021-05-03 21:20:31 +08:00
秋のかえで
a3e63e6928 style: refine style 2021-05-01 10:44:29 +08:00
秋のかえで
ec9f19039e remove v2ray.com 2021-05-01 08:55:27 +08:00
秋のかえで
ee682b17d1 Merge branch 'dns' into dns-geo 2021-05-01 08:48:51 +08:00
秋のかえで
2e56fe11e1 Merge branch 'main' into dns 2021-04-18 13:24:44 +08:00
秋のかえで
a5b2a9be2d Merge branch 'dns' into dns-geo 2021-04-15 13:27:35 +08:00
秋のかえで
d5a195458b chore: regenerate protobufs 2021-04-15 13:23:21 +08:00
秋のかえで
f8b8e8a53a Merge branch 'dns' into dns-geo 2021-04-14 13:04:45 +08:00
秋のかえで
7cf30d5101 Feat: DNS hosts support multiple addresses 2021-04-12 00:05:26 +08:00
秋のかえで
598e15aed2 style: refine var name 2021-04-11 12:45:16 +08:00
秋のかえで
708ce026ca Refine the test of dns conf 2021-04-10 17:22:44 +08:00
秋のかえで
b0a66e650c Merge branch 'dns' into dns-geo 2021-04-10 13:02:48 +08:00
JimhHan
217844cc37 Refine DNS Options 2021-04-10 11:36:58 +08:00
JimhHan
f20c445974 Fix typo 2021-04-10 00:33:55 +08:00
秋のかえで
6e902b24ae Feat: add disableFallback & skipFallback option for DNS client (#489) 2021-04-10 00:07:08 +08:00
JimhHan
70b63e21a5 Fix testing 2021-04-09 23:36:48 +08:00
JimhHan
726a722019 Refine DNS strategies 2021-04-09 23:36:36 +08:00
JimhHan
16017304b8 Merge remote-tracking branch 'upstream/dns-geo-reverse-match' into dns-geo 2021-04-09 18:48:44 +08:00
秋のかえで
9721ff7a56 Style: comment unused code 2021-04-08 23:19:43 +08:00
秋のかえで
eeb40c9ce2 Refine tests 2021-04-08 22:51:55 +08:00
JimhHan
095b17da62 Fix typo 2021-04-08 19:19:53 +08:00
JimhHan
1c3abb2ec3 Fix: config loader 2021-04-08 19:18:23 +08:00
秋のかえで
5d5beb2028 Add test to ParaseIPList 2021-04-08 13:36:35 +08:00
秋のかえで
44317bd657 Refine router config parse 2021-04-08 13:19:25 +08:00
秋のかえで
68201a8898 Feat: add reverse match for GeoIP 2021-04-08 13:07:53 +08:00
秋のかえで
8a7cd65fc2 Merge branch 'dns' into dns-geo 2021-04-07 23:17:59 +08:00
秋のかえで
f4a048aa0c Merge branch 'main' into dns 2021-04-07 23:15:45 +08:00
秋のかえで
946a7a2647 Refine DomainStrategy config 2021-04-07 22:53:21 +08:00
秋のかえで
90c81e8459 fix: fix clientIP config 2021-04-02 16:57:31 +08:00
秋のかえで
364086c974 fix: remove AA header flag in DNS query 2021-04-02 16:47:19 +08:00
秋のかえで
7a778d74d0 feat: change the handshake timeout of quic to default vaule 2021-03-31 22:55:28 +08:00
秋のかえで
d3533abe3c Merge branch 'main' into dns 2021-03-31 22:53:43 +08:00
JimhHan
b9c5de96e7 Fix: test 2021-03-29 18:57:09 +08:00
JimhHan
b8196a22a0 Fix: matching type mismatch 2021-03-29 18:57:07 +08:00
JimhHan
6471cfd290 Fix: typo 2021-03-29 18:57:05 +08:00
JimhHan
58bca70dfb Revert: move common.go to common 2021-03-29 18:57:03 +08:00
JimhHan
4eb3acb4dd Fix: format 2021-03-26 17:30:24 +08:00
JimhHan
fc8b580017 Fix: tests 2021-03-26 17:20:28 +08:00
JimhHan
06fc82bad1 Feat: sniffer exclude domain & ip 2021-03-26 16:56:43 +08:00
JimhHan
14189eba07 Replace: domain conf loader 2021-03-26 15:28:27 +08:00
JimhHan
b11429eaee Refactor: GeoSite & GeoIP 2021-03-24 23:01:20 +08:00
AkinoKaede
58daa2f788 fix: DNS tests timeout due to network instability 2021-03-22 13:20:14 +08:00
AkinoKaede
8382b29922 feat: add queryStrategy option for DNS 2021-03-19 23:35:01 +08:00
JimhHan
41d3f31447 Fix: DNS query log 2021-03-19 22:01:39 +08:00
AkinoKaede
9b93b90fa9 feat: add DNS logs to DNS over QUIC 2021-03-19 13:19:43 +08:00
AkinoKaede
b15fffaac6 fix: fix tests 2021-03-18 23:52:00 +08:00
AkinoKaede
8884e948fe refactor: new dns app 2021-03-18 23:28:01 +08:00
827 changed files with 23012 additions and 42579 deletions

View File

@@ -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 ```<details><pre><code>``` ```</code></pre></details>``` 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: |-
<details><pre><code>
</code></pre></details>
validations:
required: true
- type: textarea
attributes:
label: Server config
value: |-
<details><pre><code>
</code></pre></details>
validations:
required: true
- type: textarea
attributes:
label: Client log
value: |-
<details><pre><code>
</code></pre></details>
validations:
required: true
- type: textarea
attributes:
label: Server log
value: |-
<details><pre><code>
</code></pre></details>
validations:
required: true

View File

@@ -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 的日志,而不是面板或者别的东西输出的日志。
### 最后
在去掉不影响复现的部分后,提供实际运行的**完整**文件,不要出于自己的判断只提供入站出站或者几行日志。
把内容放在文本框预置的 ```<details><pre><code>``` 和 ```</code></pre></details>``` 中间。
如果问题十分明确只出现在某一端(如按文档正确编写配置后核心启动失败/崩溃)可以在下面不需要的项目填入N/A.
- type: textarea
attributes:
label: 客户端配置
value: |-
<details><pre><code>
</code></pre></details>
validations:
required: true
- type: textarea
attributes:
label: 服务端配置
value: |-
<details><pre><code>
</code></pre></details>
validations:
required: true
- type: textarea
attributes:
label: 客户端日志
value: |-
<details><pre><code>
</code></pre></details>
validations:
required: true
- type: textarea
attributes:
label: 服务端日志
value: |-
<details><pre><code>
</code></pre></details>
validations:
required: true

View File

@@ -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.

View File

@@ -2,6 +2,7 @@
"android-arm64": { "friendlyName": "android-arm64-v8a" }, "android-arm64": { "friendlyName": "android-arm64-v8a" },
"darwin-amd64": { "friendlyName": "macos-64" }, "darwin-amd64": { "friendlyName": "macos-64" },
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" }, "darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
"freebsd-386": { "friendlyName": "freebsd-32" }, "freebsd-386": { "friendlyName": "freebsd-32" },
"freebsd-amd64": { "friendlyName": "freebsd-64" }, "freebsd-amd64": { "friendlyName": "freebsd-64" },
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" }, "freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
@@ -21,7 +22,6 @@
"linux-ppc64le": { "friendlyName": "linux-ppc64le" }, "linux-ppc64le": { "friendlyName": "linux-ppc64le" },
"linux-ppc64": { "friendlyName": "linux-ppc64" }, "linux-ppc64": { "friendlyName": "linux-ppc64" },
"linux-riscv64": { "friendlyName": "linux-riscv64" }, "linux-riscv64": { "friendlyName": "linux-riscv64" },
"linux-loong64": { "friendlyName": "linux-loong64" },
"linux-s390x": { "friendlyName": "linux-s390x" }, "linux-s390x": { "friendlyName": "linux-s390x" },
"openbsd-386": { "friendlyName": "openbsd-32" }, "openbsd-386": { "friendlyName": "openbsd-32" },
"openbsd-amd64": { "friendlyName": "openbsd-64" }, "openbsd-amd64": { "friendlyName": "openbsd-64" },
@@ -29,6 +29,5 @@
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" }, "openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
"windows-386": { "friendlyName": "windows-32" }, "windows-386": { "friendlyName": "windows-32" },
"windows-amd64": { "friendlyName": "windows-64" }, "windows-amd64": { "friendlyName": "windows-64" },
"windows-arm64": { "friendlyName": "windows-arm64-v8a" },
"windows-arm7": { "friendlyName": "windows-arm32-v7a" } "windows-arm7": { "friendlyName": "windows-arm32-v7a" }
} }

View File

@@ -1,15 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@@ -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 <<EOF >/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/" ]

View File

@@ -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 <<EOF >/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/" ]

View File

@@ -1,133 +0,0 @@
name: Build and Push Docker Image
on:
release:
types:
- published
- released
workflow_dispatch:
inputs:
tag:
description: "Docker image tag:"
required: true
latest:
description: "Set to latest"
type: boolean
default: false
jobs:
build-and-push:
if: (github.event.action != 'published') || (github.event.action == 'published' && github.event.release.prerelease == true)
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Set repository and image name to lowercase
env:
IMAGE_NAME: "${{ github.repository }}"
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
- name: Validate and extract tag
run: |
SOURCE_TAG="${{ github.event.inputs.tag }}"
if [[ -z "$SOURCE_TAG" ]]; then
SOURCE_TAG="${{ github.ref_name }}"
fi
if [[ -z "$SOURCE_TAG" ]]; then
SOURCE_TAG="${{ github.event.release.tag_name }}"
fi
if [[ -z "$SOURCE_TAG" ]]; then
echo "Error: Could not determine a valid tag source. Input tag and context tag (github.ref_name) are both empty."
exit 1
fi
if [[ "$SOURCE_TAG" =~ ^v[0-9]+\.[0-9] ]]; then
IMAGE_TAG="${SOURCE_TAG#v}"
else
IMAGE_TAG="$SOURCE_TAG"
fi
echo "Docker image tag: '$IMAGE_TAG'."
echo "IMAGE_TAG=$IMAGE_TAG" >>${GITHUB_ENV}
LATEST=false
if [[ "${{ github.event_name }}" == "release" && "${{ github.event.release.prerelease }}" == "false" ]] || [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.latest }}" == "true" ]]; then
LATEST=true
fi
echo "Latest: '$LATEST'."
echo "LATEST=$LATEST" >>${GITHUB_ENV}
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image (main architectures)
id: build_main_arches
uses: docker/build-push-action@v6
with:
context: .
file: .github/docker/Dockerfile
platforms: |
linux/amd64
linux/arm/v7
linux/arm64/v8
linux/ppc64le
linux/s390x
provenance: false
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
- name: Build Docker image (additional architectures)
id: build_additional_arches
uses: docker/build-push-action@v6
with:
context: .
file: .github/docker/Dockerfile.usa
platforms: |
linux/386
linux/arm/v6
linux/riscv64
linux/loong64
provenance: false
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
- name: Create manifest list and push
run: |
echo "Creating multi-arch manifest with tag: '${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}'."
docker buildx imagetools create \
--tag ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }} \
${{ env.FULL_IMAGE_NAME }}@${{ steps.build_main_arches.outputs.digest }} \
${{ env.FULL_IMAGE_NAME }}@${{ steps.build_additional_arches.outputs.digest }}
if [[ "${{ env.LATEST }}" == "true" ]]; then
echo "Adding 'latest' tag to manifest: '${{ env.FULL_IMAGE_NAME }}:latest'."
docker buildx imagetools create \
--tag ${{ env.FULL_IMAGE_NAME }}:latest \
${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
fi
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
if [[ "${{ env.LATEST }}" == "true" ]]; then
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest
fi

View File

@@ -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

View File

@@ -5,71 +5,35 @@ on:
release: release:
types: [published] types: [published]
push: push:
branches:
- main
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
jobs: 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: Trigger Asset Update Workflow if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
uses: actions/github-script@v7
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);
- name: Sleep for 90 seconds if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
run: sleep 90
build: build:
needs: check-assets
permissions:
contents: write
strategy: strategy:
matrix: matrix:
# Include amd64 on all platforms. # Include amd64 on all platforms.
goos: [windows, freebsd, openbsd, linux, darwin] goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
goarch: [amd64, 386] goarch: [amd64, 386]
patch-assetname: [""]
exclude: exclude:
# Exclude i386 on darwin # Exclude i386 on darwin and dragonfly.
- goarch: 386
goos: dragonfly
- goarch: 386 - goarch: 386
goos: darwin goos: darwin
include: include:
# BEGIN MacOS ARM64 # BEIGIN MacOS ARM64
- goos: darwin - goos: darwin
goarch: arm64 goarch: arm64
# END MacOS ARM64 # END MacOS ARM64
@@ -88,26 +52,17 @@ jobs:
- goos: android - goos: android
goarch: arm64 goarch: arm64
# END Android ARM 8 # END Android ARM 8
# BEGIN Android AMD64 # Windows ARM 7
- goos: android
goarch: amd64
patch-assetname: android-amd64
# END Android AMD64
# Windows ARM
- goos: windows
goarch: arm64
- goos: windows - goos: windows
goarch: arm goarch: arm
goarm: 7 goarm: 7
# BEGIN Other architectures # BEGIN Other architectures
# BEGIN riscv64 & ARM64 & LOONG64 # BEGIN riscv64 & ARM64
- goos: linux - goos: linux
goarch: arm64 goarch: arm64
- goos: linux - goos: linux
goarch: riscv64 goarch: riscv64
- goos: linux # END riscv64 & ARM64
goarch: loong64
# END riscv64 & ARM64 & LOONG64
# BEGIN MIPS # BEGIN MIPS
- goos: linux - goos: linux
goarch: mips64 goarch: mips64
@@ -153,78 +108,75 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Set up NDK
if: matrix.goos == 'android'
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 - name: Show workflow information
id: get_filename
run: | run: |
_NAME=${{ matrix.patch-assetname }} export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
[ -n "$_NAME" ] || _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 "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
echo "::set-output name=ASSET_NAME::$_NAME"
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v2
with: with:
go-version-file: go.mod go-version: ^1.16
check-latest: true
- name: Get project dependencies - name: Get project dependencies
run: go mod download 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 - name: Build Xray
run: | run: |
mkdir -p build_assets mkdir -p build_assets
COMMID=$(git describe --always --dirty) go build -v -o build_assets/xray -trimpath -ldflags "-s -w -buildid=" ./main
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
- name: Restore Geodat Cache - name: Build Mips softfloat Xray
uses: actions/cache/restore@v4 if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
with: run: |
path: resources GOMIPS=softfloat go build -v -o build_assets/xray_softfloat -trimpath -ldflags "-s -w -buildid=" ./main
key: xray-geodat-
- name: Rename Windows Xray
- name: Copy README.md & LICENSE if: matrix.goos == 'windows'
run: |
cd ./build_assets || exit 1
mv xray xray.exe
- name: Prepare to release
run: | run: |
mv -f resources/* build_assets
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
LASTEST_TAG="$(curl -sL "https://api.github.com/repos/v2fly/${INFO[0]}/releases" | jq -r ".[0].tag_name" || echo "latest")"
FILE_NAME="${INFO[2]}.dat"
echo -e "Downloading ${FILE_NAME}..."
curl -L "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
echo -e "Verifying HASH key..."
HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
done
- name: Create ZIP archive - name: Create ZIP archive
if: github.event_name == 'release'
shell: bash shell: bash
run: | run: |
pushd build_assets || exit 1 pushd build_assets || exit 1
touch -mt $(date +%Y01010000) * touch -mt $(date +%Y01010000) *
zip -9vr ../Xray-${{ env.ASSET_NAME }}.zip . zip -9vr ../Xray-$ASSET_NAME.zip .
popd || exit 1 popd || exit 1
FILE=./Xray-${{ env.ASSET_NAME }}.zip FILE=./Xray-$ASSET_NAME.zip
DGST=$FILE.dgst DGST=$FILE.dgst
for METHOD in {"md5","sha1","sha256","sha512"} for METHOD in {"md5","sha1","sha256","sha512"}
do do
@@ -233,20 +185,20 @@ jobs:
- name: Change the name - name: Change the name
run: | run: |
mv build_assets Xray-${{ env.ASSET_NAME }} mv build_assets Xray-$ASSET_NAME
- name: Upload files to Artifacts - name: Upload files to Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: Xray-${{ env.ASSET_NAME }} name: Xray-${{ steps.get_filename.outputs.ASSET_NAME }}
path: | path: |
./Xray-${{ env.ASSET_NAME }}/* ./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}/*
- name: Upload binaries to release - name: Upload binaries to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
if: github.event_name == 'release' if: github.event_name == 'release'
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./Xray-${{ env.ASSET_NAME }}.zip* file: ./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip*
tag: ${{ github.ref }} tag: ${{ github.ref }}
file_glob: true file_glob: true

View File

@@ -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 }}

View File

@@ -2,60 +2,47 @@ name: Test
on: on:
push: push:
branches:
- main
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
jobs: 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: test:
needs: check-assets
permissions:
contents: read
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [windows-latest, ubuntu-latest, macos-latest] os: [windows-latest, ubuntu-latest, macos-latest]
steps: steps:
- name: Checkout codebase
uses: actions/checkout@v4
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v2
with: with:
go-version-file: go.mod go-version: ^1.16
check-latest: true - name: Checkout codebase
- name: Restore Geodat Cache uses: actions/checkout@v2
uses: actions/cache/restore@v4
with: - name: Prepare geo*dat
path: resources if: ${{ matrix.os != 'windows-latest' }}
key: xray-geodat- run: |
enableCrossOsArchive: true mkdir resources
wget -O ./resources/geoip.dat https://github.com/v2fly/geoip/releases/latest/download/geoip.dat
wget -O ./resources/geosite.dat https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat
- name: Prepare geo*dat for Windows
if: ${{ matrix.os == 'windows-latest' }}
run: |
mkdir resources
Invoke-WebRequest -Uri "https://github.com/v2fly/geoip/releases/latest/download/geoip.dat" -OutFile "./resources/geoip.dat"
Invoke-WebRequest -Uri "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat" -OutFile "./resources/geosite.dat"
- name: Test - name: Test
run: go test -timeout 1h -v ./... run: go test -timeout 1h -v ./...

46
.gitignore vendored
View File

@@ -1,46 +0,0 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# macOS specific files
*.DS_Store
# IDE specific files
.idea/
.vscode/
# Archive files
*.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

View File

@@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
https://t.me/projectXtls.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

227
README.md
View File

@@ -1,27 +1,82 @@
# Project X # Project X
[![Project X NFT](https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg)](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1) [Project X](https://github.com/XTLS) originates from XTLS protocol, provides a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core) and [Xray-flutter](https://github.com/XTLS/Xray-flutter).
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
[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: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
- **REALITY NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2**
- **Related links: https://opensea.io/collection/xtls, [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)**
## License ## License
[Mozilla Public License Version 2.0](https://github.com/XTLS/Xray-core/blob/main/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)
- Xray-docker
- 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)
## 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)
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch)
- Android
- [v2rayNG](https://github.com/2dust/v2rayNG)
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
- iOS / Mac
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
## Credits
This repo relies on the following third-party projects:
- Special thanks:
- [v2fly/v2ray-core](https://github.com/v2fly/v2ray-core)
- In production:
- [gorilla/websocket](https://github.com/gorilla/websocket)
- [lucas-clemente/quic-go](https://github.com/lucas-clemente/quic-go)
- [pires/go-proxyproto](https://github.com/pires/go-proxyproto)
- [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)
- [h12w/socks](https://github.com/h12w/socks)
## Compilation
### Windows
```
go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main
```
### Linux / macOS
```
go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
```
## Telegram ## Telegram
@@ -29,146 +84,6 @@
[Project X Channel](https://t.me/projectXtls) [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)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- 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)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- iOS & macOS arm64 & tvOS
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) ([tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274))
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
- [OneXray](https://github.com/OneXray/OneXray)
- macOS arm64 & x64
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
- [V2rayU](https://github.com/yanue/V2rayU)
- [V2RayXS](https://github.com/tzmax/V2RayXS)
- [Furious](https://github.com/LorenEteval/Furious)
- [OneXray](https://github.com/OneXray/OneXray)
- [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- Linux
- [v2rayA](https://github.com/v2rayA/v2rayA)
- [Furious](https://github.com/LorenEteval/Furious)
- [GorzRay](https://github.com/ketetefid/GorzRay)
- [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
- iOS & macOS arm64 & tvOS
- [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
[![Stargazers over time](https://starchart.cc/XTLS/Xray-core.svg)](https://starchart.cc/XTLS/Xray-core) [![Stargazers over time](https://starchart.cc/XTLS/Xray-core.svg)](https://starchart.cc/XTLS/Xray-core)

View File

@@ -1,16 +1,18 @@
package commander package commander
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
import ( import (
"context" "context"
"net" "net"
"sync" "sync"
"google.golang.org/grpc"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/signal/done"
core "github.com/xtls/xray-core/core" core "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/outbound"
"google.golang.org/grpc"
) )
// Commander is a Xray feature that provides gRPC methods to external clients. // Commander is a Xray feature that provides gRPC methods to external clients.
@@ -20,14 +22,12 @@ type Commander struct {
services []Service services []Service
ohm outbound.Manager ohm outbound.Manager
tag string tag string
listen string
} }
// NewCommander creates a new Commander based on the given config. // NewCommander creates a new Commander based on the given config.
func NewCommander(ctx context.Context, config *Config) (*Commander, error) { func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
c := &Commander{ c := &Commander{
tag: config.Tag, tag: config.Tag,
listen: config.Listen,
} }
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) { common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
@@ -45,7 +45,7 @@ func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
} }
service, ok := rawService.(Service) service, ok := rawService.(Service)
if !ok { if !ok {
return nil, errors.New("not a Service.") return nil, newError("not a Service.")
} }
c.services = append(c.services, service) c.services = append(c.services, service)
} }
@@ -67,32 +67,19 @@ func (c *Commander) Start() error {
} }
c.Unlock() 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{ listener := &OutboundListener{
buffer: make(chan net.Conn, 4), buffer: make(chan net.Conn, 4),
done: done.New(), 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 { 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{ return c.ohm.AddHandler(context.Background(), &Outbound{

View File

@@ -1,12 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.25.0
// protoc v5.28.2 // protoc v3.14.0
// source: app/commander/config.proto // source: app/commander/config.proto
package commander package commander
import ( import (
proto "github.com/golang/protobuf/proto"
serial "github.com/xtls/xray-core/common/serial" serial "github.com/xtls/xray-core/common/serial"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -21,6 +22,10 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Config is the settings for Commander. // Config is the settings for Commander.
type Config struct { type Config struct {
state protoimpl.MessageState state protoimpl.MessageState
@@ -29,8 +34,6 @@ type Config struct {
// Tag of the outbound handler that handles grpc connections. // Tag of the outbound handler that handles grpc connections.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
// Network address of commander grpc service.
Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"`
// Services that supported by this server. All services must implement Service // Services that supported by this server. All services must implement Service
// interface. // interface.
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"` Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
@@ -38,9 +41,11 @@ type Config struct {
func (x *Config) Reset() { func (x *Config) Reset() {
*x = Config{} *x = Config{}
mi := &file_app_commander_config_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_commander_config_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Config) String() string { func (x *Config) String() string {
@@ -51,7 +56,7 @@ func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message { func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_commander_config_proto_msgTypes[0] mi := &file_app_commander_config_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -73,13 +78,6 @@ func (x *Config) GetTag() string {
return "" return ""
} }
func (x *Config) GetListen() string {
if x != nil {
return x.Listen
}
return ""
}
func (x *Config) GetService() []*serial.TypedMessage { func (x *Config) GetService() []*serial.TypedMessage {
if x != nil { if x != nil {
return x.Service return x.Service
@@ -96,9 +94,11 @@ type ReflectionConfig struct {
func (x *ReflectionConfig) Reset() { func (x *ReflectionConfig) Reset() {
*x = ReflectionConfig{} *x = ReflectionConfig{}
mi := &file_app_commander_config_proto_msgTypes[1] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_commander_config_proto_msgTypes[1]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *ReflectionConfig) String() string { func (x *ReflectionConfig) String() string {
@@ -109,7 +109,7 @@ func (*ReflectionConfig) ProtoMessage() {}
func (x *ReflectionConfig) ProtoReflect() protoreflect.Message { func (x *ReflectionConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_commander_config_proto_msgTypes[1] mi := &file_app_commander_config_proto_msgTypes[1]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -132,21 +132,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, 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, 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, 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, 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, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73,
0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52,
0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e,
0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x33,
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -162,7 +161,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_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 (*Config)(nil), // 0: xray.app.commander.Config
(*ReflectionConfig)(nil), // 1: xray.app.commander.ReflectionConfig (*ReflectionConfig)(nil), // 1: xray.app.commander.ReflectionConfig
(*serial.TypedMessage)(nil), // 2: xray.common.serial.TypedMessage (*serial.TypedMessage)(nil), // 2: xray.common.serial.TypedMessage
@@ -181,6 +180,32 @@ func file_app_commander_config_proto_init() {
if File_app_commander_config_proto != nil { if File_app_commander_config_proto != nil {
return 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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{

View File

@@ -12,10 +12,6 @@ import "common/serial/typed_message.proto";
message Config { message Config {
// Tag of the outbound handler that handles grpc connections. // Tag of the outbound handler that handles grpc connections.
string tag = 1; string tag = 1;
// Network address of commander grpc service.
string listen = 3;
// Services that supported by this server. All services must implement Service // Services that supported by this server. All services must implement Service
// interface. // interface.
repeated xray.common.serial.TypedMessage service = 2; repeated xray.common.serial.TypedMessage service = 2;

View File

@@ -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{})
}

View File

@@ -5,10 +5,8 @@ import (
"sync" "sync"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport"
) )
@@ -33,13 +31,13 @@ func (l *OutboundListener) add(conn net.Conn) {
func (l *OutboundListener) Accept() (net.Conn, error) { func (l *OutboundListener) Accept() (net.Conn, error) {
select { select {
case <-l.done.Wait(): case <-l.done.Wait():
return nil, errors.New("listen closed") return nil, newError("listen closed")
case c := <-l.buffer: case c := <-l.buffer:
return c, nil return c, nil
} }
} }
// Close implements net.Listener. // Close implement net.Listener.
func (l *OutboundListener) Close() error { func (l *OutboundListener) Close() error {
common.Must(l.done.Close()) common.Must(l.done.Close())
L: L:
@@ -109,13 +107,3 @@ func (co *Outbound) Close() error {
co.closed = true co.closed = true
return co.listener.Close() 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
}

View File

@@ -1,12 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.25.0
// protoc v5.28.2 // protoc v3.14.0
// source: app/dispatcher/config.proto // source: app/dispatcher/config.proto
package dispatcher package dispatcher
import ( import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
@@ -20,6 +21,10 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type SessionConfig struct { type SessionConfig struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -28,9 +33,11 @@ type SessionConfig struct {
func (x *SessionConfig) Reset() { func (x *SessionConfig) Reset() {
*x = SessionConfig{} *x = SessionConfig{}
mi := &file_app_dispatcher_config_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_dispatcher_config_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *SessionConfig) String() string { func (x *SessionConfig) String() string {
@@ -41,7 +48,7 @@ func (*SessionConfig) ProtoMessage() {}
func (x *SessionConfig) ProtoReflect() protoreflect.Message { func (x *SessionConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_dispatcher_config_proto_msgTypes[0] mi := &file_app_dispatcher_config_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -66,9 +73,11 @@ type Config struct {
func (x *Config) Reset() { func (x *Config) Reset() {
*x = Config{} *x = Config{}
mi := &file_app_dispatcher_config_proto_msgTypes[1] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_dispatcher_config_proto_msgTypes[1]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Config) String() string { func (x *Config) String() string {
@@ -79,7 +88,7 @@ func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message { func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_dispatcher_config_proto_msgTypes[1] mi := &file_app_dispatcher_config_proto_msgTypes[1]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -135,7 +144,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_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 (*SessionConfig)(nil), // 0: xray.app.dispatcher.SessionConfig
(*Config)(nil), // 1: xray.app.dispatcher.Config (*Config)(nil), // 1: xray.app.dispatcher.Config
} }
@@ -153,6 +162,32 @@ func file_app_dispatcher_config_proto_init() {
if File_app_dispatcher_config_proto != nil { if File_app_dispatcher_config_proto != nil {
return 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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{

View File

@@ -1,15 +1,15 @@
package dispatcher package dispatcher
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
import ( import (
"context" "context"
"regexp"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
@@ -25,7 +25,9 @@ import (
"github.com/xtls/xray-core/transport/pipe" "github.com/xtls/xray-core/transport/pipe"
) )
var errSniffingTimeout = errors.New("timeout on sniffing") var (
errSniffingTimeout = newError("timeout on sniffing")
)
type cachedReader struct { type cachedReader struct {
sync.Mutex sync.Mutex
@@ -33,21 +35,17 @@ type cachedReader struct {
cache buf.MultiBuffer cache buf.MultiBuffer
} }
func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error { func (r *cachedReader) Cache(b *buf.Buffer) {
mb, err := r.reader.ReadMultiBufferTimeout(deadline) mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
if err != nil {
return err
}
r.Lock() r.Lock()
if !mb.IsEmpty() { if !mb.IsEmpty() {
r.cache, _ = buf.MergeMulti(r.cache, mb) r.cache, _ = buf.MergeMulti(r.cache, mb)
} }
b.Clear() b.Clear()
rawBytes := b.Extend(min(r.cache.Len(), b.Cap())) rawBytes := b.Extend(buf.Size)
n := r.cache.Copy(rawBytes) n := r.cache.Copy(rawBytes)
b.Resize(0, int32(n)) b.Resize(0, int32(n))
r.Unlock() r.Unlock()
return nil
} }
func (r *cachedReader) readInternal() buf.MultiBuffer { func (r *cachedReader) readInternal() buf.MultiBuffer {
@@ -96,16 +94,12 @@ type DefaultDispatcher struct {
router routing.Router router routing.Router
policy policy.Manager policy policy.Manager
stats stats.Manager stats stats.Manager
fdns dns.FakeDNSEngine
} }
func init() { func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
d := new(DefaultDispatcher) 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 { if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error {
core.OptionalFeatures(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)
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -177,118 +171,104 @@ 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 return inboundLink, outboundLink
} }
func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool { func shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
domain := result.Domain() domain := result.Domain()
if domain == "" { if request.ExcludedDomainMatcher != nil {
return false if request.ExcludedDomainMatcher.ApplyDomain(domain) {
} 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
}
} }
} }
var fakeDNSEngine dns.FakeDNSEngine
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
fakeDNSEngine = fdns
})
protocolString := result.Protocol() protocolString := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok { if resComp, ok := result.(SnifferResultComposite); ok {
protocolString = resComp.ProtocolForDomainResult() protocolString = resComp.ProtocolForDomainResult()
} }
for _, p := range request.OverrideDestinationForProtocol { for _, p := range request.OverrideDestinationForProtocol {
if strings.HasPrefix(protocolString, p) || strings.HasPrefix(p, protocolString) { if strings.HasPrefix(protocolString, p) {
return true return true
} }
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" && if fakeDNSEngine != nil && protocolString != "bittorrent" && p == "fakedns" &&
fkr0.IsIPInIPPool(destination.Address) { destination.Address.Family().IsIP() && fakeDNSEngine.GetFakeIPRange().Contains(destination.Address.IP()) {
errors.LogInfo(ctx, "Using sniffer ", protocolString, " since the fake DNS missed") newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
return true return true
} }
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
if resultSubset.IsProtoSubsetOf(p) {
return true
}
}
} }
return false return false
} }
func canSniff(ctx context.Context, req session.SniffingRequest, destination net.Destination) bool {
if destination.Address.Family().IsIP() && req.ExcludedIPMatcher != nil {
if req.ExcludedIPMatcher.MatchIP(destination.Address.IP()) {
return false
}
}
return true
}
// Dispatch implements routing.Dispatcher. // Dispatch implements routing.Dispatcher.
func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (*transport.Link, error) { func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (*transport.Link, error) {
if !destination.IsValid() { if !destination.IsValid() {
panic("Dispatcher: Invalid destination.") panic("Dispatcher: Invalid destination.")
} }
outbounds := session.OutboundsFromContext(ctx) ob := &session.Outbound{
if len(outbounds) == 0 { Target: destination,
outbounds = []*session.Outbound{{}}
ctx = session.ContextWithOutbounds(ctx, outbounds)
} }
ob := outbounds[len(outbounds)-1] ctx = session.ContextWithOutbound(ctx, ob)
ob.OriginalTarget = destination
ob.Target = destination inbound, outbound := d.getLink(ctx)
content := session.ContentFromContext(ctx) content := session.ContentFromContext(ctx)
if content == nil { if content == nil {
content = new(session.Content) content = new(session.Content)
ctx = session.ContextWithContent(ctx, content) ctx = session.ContextWithContent(ctx, content)
} }
sniffingRequest := content.SniffingRequest sniffingRequest := content.SniffingRequest
inbound, outbound := d.getLink(ctx)
if !sniffingRequest.Enabled { if !canSniff(ctx, sniffingRequest, destination) {
newError("skip sniffing for ip ", destination.Address.String()).AtDebug().WriteToLog()
sniffingRequest.Enabled = false
}
switch {
case !sniffingRequest.Enabled:
go d.routedDispatch(ctx, outbound, destination) go d.routedDispatch(ctx, outbound, destination)
} else { case destination.Network != net.Network_TCP:
// Only metadata sniff will be used for non tcp connection
result, err := sniffer(ctx, nil, true)
if err == nil {
content.Protocol = result.Protocol()
if shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
ob.Target = destination
}
}
go d.routedDispatch(ctx, outbound, destination)
default:
go func() { go func() {
cReader := &cachedReader{ cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader), reader: outbound.Reader.(*pipe.Reader),
} }
outbound.Reader = cReader outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network) result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
if err == nil { if err == nil {
content.Protocol = result.Protocol() content.Protocol = result.Protocol()
} }
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) { if err == nil && shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain() domain := result.Domain()
errors.LogInfo(ctx, "sniffed domain: ", domain) newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain) destination.Address = net.ParseAddress(domain)
protocol := result.Protocol() ob.Target = destination
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 {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
} }
d.routedDispatch(ctx, outbound, destination) d.routedDispatch(ctx, outbound, destination)
}() }()
@@ -296,62 +276,8 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
return inbound, nil return inbound, nil
} }
// DispatchLink implements routing.Dispatcher. func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (SniffResult, error) {
func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error { payload := buf.New()
if !destination.IsValid() {
return errors.New("Dispatcher: Invalid destination.")
}
outbounds := session.OutboundsFromContext(ctx)
if len(outbounds) == 0 {
outbounds = []*session.Outbound{{}}
ctx = session.ContextWithOutbounds(ctx, outbounds)
}
ob := outbounds[len(outbounds)-1]
ob.OriginalTarget = destination
ob.Target = destination
content := session.ContentFromContext(ctx)
if content == nil {
content = new(session.Content)
ctx = session.ContextWithContent(ctx, content)
}
sniffingRequest := content.SniffingRequest
if !sniffingRequest.Enabled {
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()
}
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 {
ob.RouteTarget = destination
} else {
ob.Target = 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)
defer payload.Release() defer payload.Release()
sniffer := NewSniffer(ctx) sniffer := NewSniffer(ctx)
@@ -363,36 +289,26 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
} }
contentResult, contentErr := func() (SniffResult, error) { contentResult, contentErr := func() (SniffResult, error) {
cacheDeadline := 200 * time.Millisecond
totalAttempt := 0 totalAttempt := 0
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return nil, ctx.Err()
default: default:
cachingStartingTimeStamp := time.Now() totalAttempt++
err := cReader.Cache(payload, cacheDeadline) if totalAttempt > 2 {
if err != nil { return nil, errSniffingTimeout
return nil, err
} }
cachingTimeElapsed := time.Since(cachingStartingTimeStamp)
cacheDeadline -= cachingTimeElapsed
cReader.Cache(payload)
if !payload.IsEmpty() { if !payload.IsEmpty() {
result, err := sniffer.Sniff(ctx, payload.Bytes(), network) result, err := sniffer.Sniff(ctx, payload.Bytes())
switch err { if err != common.ErrNoClue {
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:
return result, err return result, err
} }
} else {
totalAttempt++
} }
if totalAttempt >= 2 || cacheDeadline <= 0 { if payload.IsFull() {
return nil, errSniffingTimeout return nil, errUnknownContent
} }
} }
} }
@@ -405,43 +321,25 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
} }
return contentResult, contentErr return contentResult, contentErr
} }
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
var handler outbound.Handler var handler outbound.Handler
routingLink := routing_session.AsRoutingContext(ctx) routingLink := routing_session.AsRoutingContext(ctx)
inTag := routingLink.GetInboundTag() inTag := routingLink.GetInboundTag()
isPickRoute := 0 isPickRoute := false
if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" { if d.router != nil {
ctx = session.SetForcedOutboundTagToContext(ctx, "")
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
isPickRoute = 1
errors.LogInfo(ctx, "taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]")
handler = h
} else {
errors.LogError(ctx, "non existing tag for platform initialized detour: ", forcedOutboundTag)
common.Close(link.Writer)
common.Interrupt(link.Reader)
return
}
} else if d.router != nil {
if route, err := d.router.PickRoute(routingLink); err == nil { if route, err := d.router.PickRoute(routingLink); err == nil {
outTag := route.GetOutboundTag() outTag := route.GetOutboundTag()
isPickRoute = true
if h := d.ohm.GetHandler(outTag); h != nil { if h := d.ohm.GetHandler(outTag); h != nil {
isPickRoute = 2 newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
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, "]")
}
handler = h handler = h
} else { } else {
errors.LogWarning(ctx, "non existing outTag: ", outTag) newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
} }
} else { } else {
errors.LogInfo(ctx, "default route for ", destination) newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
} }
} }
@@ -450,23 +348,26 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
} }
if handler == nil { 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.Close(link.Writer)
common.Interrupt(link.Reader) common.Interrupt(link.Reader)
return return
} }
ob.Tag = handler.Tag()
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil { if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
if tag := handler.Tag(); tag != "" { if tag := handler.Tag(); tag != "" {
if inTag == "" { if isPickRoute {
accessMessage.Detour = tag if inTag != "" {
} else if isPickRoute == 1 { accessMessage.Detour = inTag + " -> " + tag
accessMessage.Detour = inTag + " ==> " + tag } else {
} else if isPickRoute == 2 { accessMessage.Detour = tag
accessMessage.Detour = inTag + " -> " + tag }
} else { } else {
accessMessage.Detour = inTag + " >> " + tag if inTag != "" {
accessMessage.Detour = inTag + " >> " + tag
} else {
accessMessage.Detour = tag
}
} }
} }
log.Record(accessMessage) log.Record(accessMessage)

View File

@@ -1 +1,3 @@
package dispatcher package dispatcher
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen

View File

@@ -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{})
}

View File

@@ -2,49 +2,36 @@ package dispatcher
import ( import (
"context" "context"
"strings"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
// newFakeDNSSniffer Creates a Fake DNS metadata sniffer // newFakeDNSSniffer Create a Fake DNS metadata sniffer
func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) { func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {
var fakeDNSEngine dns.FakeDNSEngine var fakeDNSEngine dns.FakeDNSEngine
{ err := core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
fakeDNSEngineFeat := core.MustFromContext(ctx).GetFeature((*dns.FakeDNSEngine)(nil)) fakeDNSEngine = fdns
if fakeDNSEngineFeat != nil { })
fakeDNSEngine = fakeDNSEngineFeat.(dns.FakeDNSEngine) if err != nil {
} return protocolSnifferWithMetadata{}, err
} }
if fakeDNSEngine == nil { 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{}, errNotInit
} }
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) { return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
outbounds := session.OutboundsFromContext(ctx) Target := session.OutboundFromContext(ctx).Target
ob := outbounds[len(outbounds)-1] if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP { domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address)
if domainFromFakeDNS != "" { 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 return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
} }
} }
if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
inPool := fkr0.IsIPInIPPool(ob.Target.Address)
ipAddressInRangeValue.addressInRange = &inPool
}
}
return nil, common.ErrNoClue return nil, common.ErrNoClue
}, metadataSniffer: true}, nil }, metadataSniffer: true}, nil
} }
@@ -60,62 +47,3 @@ func (fakeDNSSniffResult) Protocol() string {
func (f fakeDNSSniffResult) Domain() string { func (f fakeDNSSniffResult) Domain() string {
return f.domainName return f.domainName
} }
type fakeDNSExtraOpts int
const ipAddressInRange fakeDNSExtraOpts = 1
type ipAddressInRangeOpt struct {
addressInRange *bool
}
type DNSThenOthersSniffResult struct {
domainName string
protocolOriginalName string
}
func (f DNSThenOthersSniffResult) IsProtoSubsetOf(protocolName string) bool {
return strings.HasPrefix(protocolName, f.protocolOriginalName)
}
func (DNSThenOthersSniffResult) Protocol() string {
return "fakedns+others"
}
func (f DNSThenOthersSniffResult) Domain() string {
return f.domainName
}
func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWithMetadata, others []protocolSnifferWithMetadata) (
protocolSnifferWithMetadata, error,
) { // nolint: unparam
// ctx may be used in the future
_ = ctx
return protocolSnifferWithMetadata{
protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
ipAddressInRangeValue := &ipAddressInRangeOpt{}
ctx = context.WithValue(ctx, ipAddressInRange, ipAddressInRangeValue)
result, err := fakeDNSSniffer.protocolSniffer(ctx, bytes)
if err == nil {
return result, nil
}
if ipAddressInRangeValue.addressInRange != nil {
if *ipAddressInRangeValue.addressInRange {
for _, v := range others {
if v.metadataSniffer || bytes != nil {
if result, err := v.protocolSniffer(ctx, bytes); err == nil {
return DNSThenOthersSniffResult{domainName: result.Domain(), protocolOriginalName: result.Protocol()}, nil
}
}
}
return nil, common.ErrNoClue
}
errors.LogDebug(ctx, "ip address not in fake dns range, return as is")
return nil, common.ErrNoClue
}
errors.LogWarning(ctx, "fake dns sniffer did not set address in range option, assume false.")
return nil, common.ErrNoClue
},
metadataSniffer: false,
}, nil
}

View File

@@ -4,12 +4,8 @@ import (
"context" "context"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/protocol/bittorrent" "github.com/xtls/xray-core/common/protocol/bittorrent"
"github.com/xtls/xray-core/common/protocol/http" "github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/protocol/quic"
"github.com/xtls/xray-core/common/protocol/tls" "github.com/xtls/xray-core/common/protocol/tls"
) )
@@ -26,7 +22,6 @@ type protocolSnifferWithMetadata struct {
// for both TCP and UDP connections // for both TCP and UDP connections
// It will not be shown as a traffic type for routing unless there is no other successful sniffing. // It will not be shown as a traffic type for routing unless there is no other successful sniffing.
metadataSniffer bool metadataSniffer bool
network net.Network
} }
type Sniffer struct { type Sniffer struct {
@@ -36,40 +31,30 @@ type Sniffer struct {
func NewSniffer(ctx context.Context) *Sniffer { func NewSniffer(ctx context.Context) *Sniffer {
ret := &Sniffer{ ret := &Sniffer{
sniffer: []protocolSnifferWithMetadata{ 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},
{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 tls.SniffTLS(b) }, false},
{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 bittorrent.SniffBittorrent(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},
}, },
} }
if sniffer, err := newFakeDNSSniffer(ctx); err == nil { if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
others := ret.sniffer
ret.sniffer = append(ret.sniffer, sniffer) ret.sniffer = append(ret.sniffer, sniffer)
fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others)
if err == nil {
ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...)
}
} }
return ret 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) { func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
var pendingSniffer []protocolSnifferWithMetadata var pendingSniffer []protocolSnifferWithMetadata
for _, si := range s.sniffer { for _, si := range s.sniffer {
protocolSniffer := si.protocolSniffer s := si.protocolSniffer
if si.metadataSniffer || si.network != network { if si.metadataSniffer {
continue continue
} }
result, err := protocolSniffer(c, payload) result, err := s(c, payload)
if err == common.ErrNoClue { if err == common.ErrNoClue {
pendingSniffer = append(pendingSniffer, si) pendingSniffer = append(pendingSniffer, si)
continue 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 { if err == nil && result != nil {
@@ -136,7 +121,3 @@ func (c compositeResult) ProtocolForDomainResult() string {
type SnifferResultComposite interface { type SnifferResultComposite interface {
ProtocolForDomainResult() string ProtocolForDomainResult() string
} }
type SnifferIsProtoSubsetOf interface {
IsProtoSubsetOf(protocolName string) bool
}

View File

@@ -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()
}
}

View File

@@ -1,32 +1,32 @@
package dns package dns
import ( import (
"github.com/xtls/xray-core/common/errors" dm "github.com/xtls/xray-core/common/matcher/domain"
"github.com/xtls/xray-core/common/matcher/str"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/common/uuid"
) )
var typeMap = map[DomainMatchingType]strmatcher.Type{ var typeMap = map[dm.MatchingType]str.Type{
DomainMatchingType_Full: strmatcher.Full, dm.MatchingType_Keyword: str.Substr,
DomainMatchingType_Subdomain: strmatcher.Domain, dm.MatchingType_Regex: str.Regex,
DomainMatchingType_Keyword: strmatcher.Substr, dm.MatchingType_Subdomain: str.Domain,
DomainMatchingType_Regex: strmatcher.Regex, dm.MatchingType_Full: str.Full,
} }
// References: // References:
// https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml // https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
// https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan // https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan
var localTLDsAndDotlessDomains = []*NameServer_PriorityDomain{ var localTLDsAndDotlessDomains = []*dm.Domain{
{Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot {Type: dm.MatchingType_Regex, Value: "^[^.]+$"}, // This will only match domains without any dot
{Type: DomainMatchingType_Subdomain, Domain: "local"}, {Type: dm.MatchingType_Subdomain, Value: "local"},
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"}, {Type: dm.MatchingType_Subdomain, Value: "localdomain"},
{Type: DomainMatchingType_Subdomain, Domain: "localhost"}, {Type: dm.MatchingType_Subdomain, Value: "localhost"},
{Type: DomainMatchingType_Subdomain, Domain: "lan"}, {Type: dm.MatchingType_Subdomain, Value: "lan"},
{Type: DomainMatchingType_Subdomain, Domain: "home.arpa"}, {Type: dm.MatchingType_Subdomain, Value: "home.arpa"},
{Type: DomainMatchingType_Subdomain, Domain: "example"}, {Type: dm.MatchingType_Subdomain, Value: "example"},
{Type: DomainMatchingType_Subdomain, Domain: "invalid"}, {Type: dm.MatchingType_Subdomain, Value: "invalid"},
{Type: DomainMatchingType_Subdomain, Domain: "test"}, {Type: dm.MatchingType_Subdomain, Value: "test"},
} }
var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{ var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{
@@ -34,14 +34,14 @@ var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{
Size: uint32(len(localTLDsAndDotlessDomains)), Size: uint32(len(localTLDsAndDotlessDomains)),
} }
func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) { func toStrMatcher(t dm.MatchingType, domain string) (str.Matcher, error) {
strMType, f := typeMap[t] strMType, f := typeMap[t]
if !f { if !f {
return nil, errors.New("unknown mapping type", t).AtWarning() return nil, newError("unknown mapping type", t).AtWarning()
} }
matcher, err := strMType.New(domain) matcher, err := strMType.New(domain)
if err != nil { 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 return matcher, nil
} }
@@ -52,7 +52,7 @@ func toNetIP(addrs []net.Address) ([]net.IP, error) {
if addr.Family().IsIP() { if addr.Family().IsIP() {
ips = append(ips, addr.IP()) ips = append(ips, addr.IP())
} else { } 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 return ips, nil

View File

@@ -1,13 +1,14 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.26.0
// protoc v5.28.2 // protoc v3.15.8
// source: app/dns/config.proto // source: app/dns/config.proto
package dns package dns
import ( import (
router "github.com/xtls/xray-core/app/router" domain "github.com/xtls/xray-core/common/matcher/domain"
geoip "github.com/xtls/xray-core/common/matcher/geoip"
net "github.com/xtls/xray-core/common/net" net "github.com/xtls/xray-core/common/net"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -22,65 +23,12 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type DomainMatchingType int32
const (
DomainMatchingType_Full DomainMatchingType = 0
DomainMatchingType_Subdomain DomainMatchingType = 1
DomainMatchingType_Keyword DomainMatchingType = 2
DomainMatchingType_Regex DomainMatchingType = 3
)
// Enum value maps for DomainMatchingType.
var (
DomainMatchingType_name = map[int32]string{
0: "Full",
1: "Subdomain",
2: "Keyword",
3: "Regex",
}
DomainMatchingType_value = map[string]int32{
"Full": 0,
"Subdomain": 1,
"Keyword": 2,
"Regex": 3,
}
)
func (x DomainMatchingType) Enum() *DomainMatchingType {
p := new(DomainMatchingType)
*p = x
return p
}
func (x DomainMatchingType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (DomainMatchingType) Descriptor() protoreflect.EnumDescriptor {
return file_app_dns_config_proto_enumTypes[0].Descriptor()
}
func (DomainMatchingType) Type() protoreflect.EnumType {
return &file_app_dns_config_proto_enumTypes[0]
}
func (x DomainMatchingType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use DomainMatchingType.Descriptor instead.
func (DomainMatchingType) EnumDescriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
}
type QueryStrategy int32 type QueryStrategy int32
const ( const (
QueryStrategy_USE_IP QueryStrategy = 0 QueryStrategy_USE_IP QueryStrategy = 0
QueryStrategy_USE_IP4 QueryStrategy = 1 QueryStrategy_USE_IP4 QueryStrategy = 1
QueryStrategy_USE_IP6 QueryStrategy = 2 QueryStrategy_USE_IP6 QueryStrategy = 2
QueryStrategy_USE_SYS QueryStrategy = 3
) )
// Enum value maps for QueryStrategy. // Enum value maps for QueryStrategy.
@@ -89,13 +37,11 @@ var (
0: "USE_IP", 0: "USE_IP",
1: "USE_IP4", 1: "USE_IP4",
2: "USE_IP6", 2: "USE_IP6",
3: "USE_SYS",
} }
QueryStrategy_value = map[string]int32{ QueryStrategy_value = map[string]int32{
"USE_IP": 0, "USE_IP": 0,
"USE_IP4": 1, "USE_IP4": 1,
"USE_IP6": 2, "USE_IP6": 2,
"USE_SYS": 3,
} }
) )
@@ -110,11 +56,11 @@ func (x QueryStrategy) String() string {
} }
func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor { func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_app_dns_config_proto_enumTypes[1].Descriptor() return file_app_dns_config_proto_enumTypes[0].Descriptor()
} }
func (QueryStrategy) Type() protoreflect.EnumType { func (QueryStrategy) Type() protoreflect.EnumType {
return &file_app_dns_config_proto_enumTypes[1] return &file_app_dns_config_proto_enumTypes[0]
} }
func (x QueryStrategy) Number() protoreflect.EnumNumber { func (x QueryStrategy) Number() protoreflect.EnumNumber {
@@ -123,6 +69,55 @@ func (x QueryStrategy) Number() protoreflect.EnumNumber {
// Deprecated: Use QueryStrategy.Descriptor instead. // Deprecated: Use QueryStrategy.Descriptor instead.
func (QueryStrategy) EnumDescriptor() ([]byte, []int) { func (QueryStrategy) EnumDescriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
}
type CacheStrategy int32
const (
CacheStrategy_Cache_ALL CacheStrategy = 0
CacheStrategy_Cache_NOERROR CacheStrategy = 1
CacheStrategy_Cache_DISABLE CacheStrategy = 2
)
// Enum value maps for CacheStrategy.
var (
CacheStrategy_name = map[int32]string{
0: "Cache_ALL",
1: "Cache_NOERROR",
2: "Cache_DISABLE",
}
CacheStrategy_value = map[string]int32{
"Cache_ALL": 0,
"Cache_NOERROR": 1,
"Cache_DISABLE": 2,
}
)
func (x CacheStrategy) Enum() *CacheStrategy {
p := new(CacheStrategy)
*p = x
return p
}
func (x CacheStrategy) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (CacheStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_app_dns_config_proto_enumTypes[1].Descriptor()
}
func (CacheStrategy) Type() protoreflect.EnumType {
return &file_app_dns_config_proto_enumTypes[1]
}
func (x CacheStrategy) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use CacheStrategy.Descriptor instead.
func (CacheStrategy) EnumDescriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{1} return file_app_dns_config_proto_rawDescGZIP(), []int{1}
} }
@@ -131,27 +126,21 @@ type NameServer struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` 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"` 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"` PrioritizedDomain []*domain.Domain `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 []*geoip.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"` 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() { func (x *NameServer) Reset() {
*x = NameServer{} *x = NameServer{}
mi := &file_app_dns_config_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_dns_config_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *NameServer) String() string { func (x *NameServer) String() string {
@@ -162,7 +151,7 @@ func (*NameServer) ProtoMessage() {}
func (x *NameServer) ProtoReflect() protoreflect.Message { func (x *NameServer) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[0] mi := &file_app_dns_config_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -198,16 +187,16 @@ func (x *NameServer) GetSkipFallback() bool {
return false return false
} }
func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain { func (x *NameServer) GetPrioritizedDomain() []*domain.Domain {
if x != nil { if x != nil {
return x.PrioritizedDomain return x.PrioritizedDomain
} }
return nil return nil
} }
func (x *NameServer) GetExpectedGeoip() []*router.GeoIP { func (x *NameServer) GetGeoip() []*geoip.GeoIP {
if x != nil { if x != nil {
return x.ExpectedGeoip return x.Geoip
} }
return nil return nil
} }
@@ -219,70 +208,24 @@ func (x *NameServer) GetOriginalRules() []*NameServer_OriginalRule {
return nil 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 { type Config struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields 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. // 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"` 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 // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes
// (IPv6). // (IPv6).
ClientIp []byte `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` ClientIp []byte `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
@@ -290,17 +233,18 @@ type Config struct {
// Tag is the inbound tag of DNS client. // Tag is the inbound tag of DNS client.
Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"` Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
// DisableCache disables DNS cache // DisableCache disables DNS cache
DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"` CacheStrategy CacheStrategy `protobuf:"varint,8,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=xray.app.dns.CacheStrategy" json:"cache_strategy,omitempty"`
QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"` DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
*x = Config{} *x = Config{}
mi := &file_app_dns_config_proto_msgTypes[1] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_dns_config_proto_msgTypes[1]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Config) String() string { func (x *Config) String() string {
@@ -311,7 +255,7 @@ func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message { func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[1] mi := &file_app_dns_config_proto_msgTypes[1]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -326,6 +270,14 @@ func (*Config) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{1} 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 { func (x *Config) GetNameServer() []*NameServer {
if x != nil { if x != nil {
return x.NameServer return x.NameServer
@@ -333,6 +285,14 @@ func (x *Config) GetNameServer() []*NameServer {
return nil 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 { func (x *Config) GetClientIp() []byte {
if x != nil { if x != nil {
return x.ClientIp return x.ClientIp
@@ -354,11 +314,11 @@ func (x *Config) GetTag() string {
return "" return ""
} }
func (x *Config) GetDisableCache() bool { func (x *Config) GetCacheStrategy() CacheStrategy {
if x != nil { if x != nil {
return x.DisableCache return x.CacheStrategy
} }
return false return CacheStrategy_Cache_ALL
} }
func (x *Config) GetQueryStrategy() QueryStrategy { func (x *Config) GetQueryStrategy() QueryStrategy {
@@ -375,66 +335,6 @@ func (x *Config) GetDisableFallback() bool {
return false return false
} }
func (x *Config) GetDisableFallbackIfMatch() bool {
if x != nil {
return x.DisableFallbackIfMatch
}
return false
}
type NameServer_PriorityDomain struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.dns.DomainMatchingType" json:"type,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
}
func (x *NameServer_PriorityDomain) Reset() {
*x = NameServer_PriorityDomain{}
mi := &file_app_dns_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NameServer_PriorityDomain) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NameServer_PriorityDomain) ProtoMessage() {}
func (x *NameServer_PriorityDomain) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NameServer_PriorityDomain.ProtoReflect.Descriptor instead.
func (*NameServer_PriorityDomain) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0, 0}
}
func (x *NameServer_PriorityDomain) GetType() DomainMatchingType {
if x != nil {
return x.Type
}
return DomainMatchingType_Full
}
func (x *NameServer_PriorityDomain) GetDomain() string {
if x != nil {
return x.Domain
}
return ""
}
type NameServer_OriginalRule struct { type NameServer_OriginalRule struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -446,9 +346,11 @@ type NameServer_OriginalRule struct {
func (x *NameServer_OriginalRule) Reset() { func (x *NameServer_OriginalRule) Reset() {
*x = NameServer_OriginalRule{} *x = NameServer_OriginalRule{}
mi := &file_app_dns_config_proto_msgTypes[3] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_dns_config_proto_msgTypes[2]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *NameServer_OriginalRule) String() string { func (x *NameServer_OriginalRule) String() string {
@@ -458,8 +360,8 @@ func (x *NameServer_OriginalRule) String() string {
func (*NameServer_OriginalRule) ProtoMessage() {} func (*NameServer_OriginalRule) ProtoMessage() {}
func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message { func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[3] mi := &file_app_dns_config_proto_msgTypes[2]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -471,7 +373,7 @@ func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message {
// Deprecated: Use NameServer_OriginalRule.ProtoReflect.Descriptor instead. // Deprecated: Use NameServer_OriginalRule.ProtoReflect.Descriptor instead.
func (*NameServer_OriginalRule) Descriptor() ([]byte, []int) { func (*NameServer_OriginalRule) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0, 1} return file_app_dns_config_proto_rawDescGZIP(), []int{0, 0}
} }
func (x *NameServer_OriginalRule) GetRule() string { func (x *NameServer_OriginalRule) GetRule() string {
@@ -493,9 +395,9 @@ type Config_HostMapping struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.dns.DomainMatchingType" json:"type,omitempty"` Type domain.MatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=xray.common.matcher.domain.MatchingType" json:"type,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"` Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
// ProxiedDomain indicates the mapped domain has the same IP address on this // ProxiedDomain indicates the mapped domain has the same IP address on this
// domain. Xray will use this domain for IP queries. // domain. Xray will use this domain for IP queries.
ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"` ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
@@ -503,9 +405,11 @@ type Config_HostMapping struct {
func (x *Config_HostMapping) Reset() { func (x *Config_HostMapping) Reset() {
*x = Config_HostMapping{} *x = Config_HostMapping{}
mi := &file_app_dns_config_proto_msgTypes[4] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_dns_config_proto_msgTypes[4]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Config_HostMapping) String() string { func (x *Config_HostMapping) String() string {
@@ -516,7 +420,7 @@ func (*Config_HostMapping) ProtoMessage() {}
func (x *Config_HostMapping) ProtoReflect() protoreflect.Message { func (x *Config_HostMapping) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[4] mi := &file_app_dns_config_proto_msgTypes[4]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -528,14 +432,14 @@ func (x *Config_HostMapping) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config_HostMapping.ProtoReflect.Descriptor instead. // Deprecated: Use Config_HostMapping.ProtoReflect.Descriptor instead.
func (*Config_HostMapping) Descriptor() ([]byte, []int) { 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 { func (x *Config_HostMapping) GetType() domain.MatchingType {
if x != nil { if x != nil {
return x.Type return x.Type
} }
return DomainMatchingType_Full return domain.MatchingType_Full
} }
func (x *Config_HostMapping) GetDomain() string { func (x *Config_HostMapping) GetDomain() string {
@@ -564,109 +468,99 @@ var File_app_dns_config_proto protoreflect.FileDescriptor
var file_app_dns_config_proto_rawDesc = []byte{ var file_app_dns_config_proto_rawDesc = []byte{
0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 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, 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x18, 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, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c,
0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x06, 0x0a, 0x0a, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x63, 0x6f,
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x64, 0x6f, 0x6d,
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x69, 0x6e, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2e, 0x70, 0x72, 0x6f,
0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x74, 0x6f, 0x22, 0x93, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x07, 0x61,
0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46,
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x51, 0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72,
0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3d, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20,
0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x6e, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74,
0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x36, 0x0a, 0x05, 0x67, 0x65,
0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e,
0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f,
0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72,
0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c,
0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73,
0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65,
0x50, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x50, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xdf, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f,
0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x6e, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x76, 0x65, 0x72, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72,
0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x39, 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f,
0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f,
0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42,
0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e, 0x02, 0x18, 0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c,
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, 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, 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, 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, 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, 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, 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, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x42,
0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
0x67, 0x79, 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, 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, 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, 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, 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, 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, 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, 0x1a, 0x55, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65,
0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x9a, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74,
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x3c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x64, 0x6f, 0x6d, 0x61,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x69, 0x6e, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52,
0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a,
0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a,
0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f,
0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a, 0x35, 0x0a, 0x0d, 0x51, 0x75,
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, 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, 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, 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, 0x02, 0x2a, 0x44, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x67, 0x79, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x41, 0x4c, 0x4c, 0x10,
0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x4e, 0x4f, 0x45, 0x52, 0x52,
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x44, 0x49,
0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78,
0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73,
0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -683,34 +577,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_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_app_dns_config_proto_goTypes = []any{ var file_app_dns_config_proto_goTypes = []interface{}{
(DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType (QueryStrategy)(0), // 0: xray.app.dns.QueryStrategy
(QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy (CacheStrategy)(0), // 1: xray.app.dns.CacheStrategy
(*NameServer)(nil), // 2: xray.app.dns.NameServer (*NameServer)(nil), // 2: xray.app.dns.NameServer
(*Config)(nil), // 3: xray.app.dns.Config (*Config)(nil), // 3: xray.app.dns.Config
(*NameServer_PriorityDomain)(nil), // 4: xray.app.dns.NameServer.PriorityDomain (*NameServer_OriginalRule)(nil), // 4: xray.app.dns.NameServer.OriginalRule
(*NameServer_OriginalRule)(nil), // 5: xray.app.dns.NameServer.OriginalRule nil, // 5: xray.app.dns.Config.HostsEntry
(*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping (*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping
(*net.Endpoint)(nil), // 7: xray.common.net.Endpoint (*net.Endpoint)(nil), // 7: xray.common.net.Endpoint
(*router.GeoIP)(nil), // 8: xray.app.router.GeoIP (*domain.Domain)(nil), // 8: xray.common.matcher.domain.Domain
(*geoip.GeoIP)(nil), // 9: xray.common.matcher.geoip.GeoIP
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
(domain.MatchingType)(0), // 11: xray.common.matcher.domain.MatchingType
} }
var file_app_dns_config_proto_depIdxs = []int32{ var file_app_dns_config_proto_depIdxs = []int32{
7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint 7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
4, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain 8, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.common.matcher.domain.Domain
8, // 2: xray.app.dns.NameServer.expected_geoip:type_name -> xray.app.router.GeoIP 9, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.common.matcher.geoip.GeoIP
5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule 4, // 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 7, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
8, // 5: xray.app.dns.NameServer.unexpected_geoip:type_name -> xray.app.router.GeoIP 2, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
2, // 6: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer 5, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping 6, // 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 1, // 8: xray.app.dns.Config.cache_strategy:type_name -> xray.app.dns.CacheStrategy
0, // 9: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType 0, // 9: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType 10, // 10: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
11, // [11:11] is the sub-list for method output_type 11, // 11: xray.app.dns.Config.HostMapping.type:type_name -> xray.common.matcher.domain.MatchingType
11, // [11:11] is the sub-list for method input_type 12, // [12:12] is the sub-list for method output_type
11, // [11:11] is the sub-list for extension type_name 12, // [12:12] is the sub-list for method input_type
11, // [11:11] is the sub-list for extension extendee 12, // [12:12] is the sub-list for extension type_name
0, // [0:11] is the sub-list for field 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() } func init() { file_app_dns_config_proto_init() }
@@ -718,6 +616,56 @@ func file_app_dns_config_proto_init() {
if File_app_dns_config_proto != nil { if File_app_dns_config_proto != nil {
return 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_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[4].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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{

View File

@@ -6,62 +6,57 @@ option go_package = "github.com/xtls/xray-core/app/dns";
option java_package = "com.xray.app.dns"; option java_package = "com.xray.app.dns";
option java_multiple_files = true; option java_multiple_files = true;
import "common/net/address.proto";
import "common/net/destination.proto"; import "common/net/destination.proto";
import "app/router/config.proto"; import "common/matcher/domain/domain.proto";
import "common/matcher/geoip/geoip.proto";
message NameServer { message NameServer {
xray.common.net.Endpoint address = 1; xray.common.net.Endpoint address = 1;
bytes client_ip = 5; bytes client_ip = 5;
bool skipFallback = 6; bool skipFallback = 6;
message PriorityDomain {
DomainMatchingType type = 1;
string domain = 2;
}
message OriginalRule { message OriginalRule {
string rule = 1; string rule = 1;
uint32 size = 2; uint32 size = 2;
} }
repeated PriorityDomain prioritized_domain = 2; repeated xray.common.matcher.domain.Domain prioritized_domain = 2;
repeated xray.app.router.GeoIP expected_geoip = 3; repeated xray.common.matcher.geoip.GeoIP geoip = 3;
repeated OriginalRule original_rules = 4; 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 {
Full = 0;
Subdomain = 1;
Keyword = 2;
Regex = 3;
} }
enum QueryStrategy { enum QueryStrategy {
USE_IP = 0; USE_IP = 0;
USE_IP4 = 1; USE_IP4 = 1;
USE_IP6 = 2; USE_IP6 = 2;
USE_SYS = 3; }
enum CacheStrategy {
Cache_ALL = 0;
Cache_NOERROR = 1;
Cache_DISABLE = 2;
} }
message Config { 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. // 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; repeated NameServer name_server = 5;
// Static hosts. Domain to IP.
// Deprecated. Use static_hosts.
map<string, xray.common.net.IPOrDomain> Hosts = 2 [deprecated = true];
// Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes
// (IPv6). // (IPv6).
bytes client_ip = 3; bytes client_ip = 3;
message HostMapping { message HostMapping {
DomainMatchingType type = 1; xray.common.matcher.domain.MatchingType type = 1;
string domain = 2; string domain = 2;
repeated bytes ip = 3; repeated bytes ip = 3;
@@ -79,10 +74,9 @@ message Config {
reserved 7; reserved 7;
// DisableCache disables DNS cache // DisableCache disables DNS cache
bool disableCache = 8; CacheStrategy cache_strategy = 8;
QueryStrategy query_strategy = 9; QueryStrategy query_strategy = 9;
bool disableFallback = 10; bool disableFallback = 10;
bool disableFallbackIfMatch = 11;
} }

View File

@@ -1,34 +1,33 @@
// Package dns is an implementation of core.DNS feature. // Package dns is an implementation of core.DNS feature.
package dns package dns
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
import ( import (
"context" "context"
go_errors "errors"
"fmt" "fmt"
"sort"
"strings" "strings"
"sync" "sync"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/matcher/geoip"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/strmatcher" "github.com/xtls/xray-core/features"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
// DNS is a DNS rely server. // DNS is a DNS rely server.
type DNS struct { type DNS struct {
sync.Mutex sync.Mutex
disableFallback bool tag string
disableFallbackIfMatch bool cacheStrategy CacheStrategy
ipOption *dns.IPOption disableFallback bool
hosts *StaticHosts ipOption *dns.IPOption
clients []*Client hosts *StaticHosts
ctx context.Context clients []*Client
domainMatcher strmatcher.IndexMatcher ctx context.Context
matcherInfos []*DomainMatcherInfo
checkSystem bool
} }
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
@@ -39,117 +38,92 @@ type DomainMatcherInfo struct {
// New creates a new DNS server with given configuration. // New creates a new DNS server with given configuration.
func New(ctx context.Context, config *Config) (*DNS, error) { 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 var clientIP net.IP
switch len(config.ClientIp) { switch len(config.ClientIp) {
case 0, net.IPv4len, net.IPv6len: case 0, net.IPv4len, net.IPv6len:
clientIP = net.IP(config.ClientIp) clientIP = net.IP(config.ClientIp)
default: 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 var ipOption *dns.IPOption
checkSystem := false
switch config.QueryStrategy { switch config.QueryStrategy {
case QueryStrategy_USE_IP: case QueryStrategy_USE_IP:
ipOption = dns.IPOption{ ipOption = &dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
FakeEnable: false, FakeEnable: false,
} }
case QueryStrategy_USE_SYS:
ipOption = dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}
checkSystem = true
case QueryStrategy_USE_IP4: case QueryStrategy_USE_IP4:
ipOption = dns.IPOption{ ipOption = &dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: false, IPv6Enable: false,
FakeEnable: false, FakeEnable: false,
} }
case QueryStrategy_USE_IP6: case QueryStrategy_USE_IP6:
ipOption = dns.IPOption{ ipOption = &dns.IPOption{
IPv4Enable: false, IPv4Enable: false,
IPv6Enable: true, IPv6Enable: true,
FakeEnable: false, 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 { 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 domainRuleCount := 0
var defaultTag = config.Tag
if len(config.Tag) == 0 {
defaultTag = generateRandomTag()
}
for _, ns := range config.NameServer { for _, ns := range config.NameServer {
domainRuleCount += len(ns.PrioritizedDomain) domainRuleCount += len(ns.PrioritizedDomain)
} }
// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1 geoipContainer := geoip.GeoIPMatcherContainer{}
matcherInfos := make([]*DomainMatcherInfo, domainRuleCount+1)
domainMatcher := &strmatcher.MatcherGroup{} 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 { for _, ns := range config.NameServer {
clientIdx := len(clients)
updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int, matcherInfos []*DomainMatcherInfo) error {
midx := domainMatcher.Add(domainRule)
matcherInfos[midx] = &DomainMatcherInfo{
clientIdx: uint16(clientIdx),
domainRuleIdx: uint16(originalRuleIdx),
}
return nil
}
myClientIP := clientIP myClientIP := clientIP
switch len(ns.ClientIp) { switch len(ns.ClientIp) {
case net.IPv4len, net.IPv6len: case net.IPv4len, net.IPv6len:
myClientIP = net.IP(ns.ClientIp) myClientIP = ns.ClientIp
} }
client, err := NewClient(ctx, ns, myClientIP, geoipContainer)
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)
if err != nil { 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) clients = append(clients, client)
} }
// If there is no DNS client in config, add a `localhost` DNS client // If there is no DNS client in config, add a `localhost` DNS client
if len(clients) == 0 { if len(clients) == 0 {
clients = append(clients, NewLocalDNSClient(ipOption)) clients = append(clients, NewLocalDNSClient())
} }
return &DNS{ return &DNS{
hosts: hosts, tag: tag,
ipOption: &ipOption, hosts: hosts,
clients: clients, ipOption: ipOption,
ctx: ctx, clients: clients,
domainMatcher: domainMatcher, ctx: ctx,
matcherInfos: matcherInfos, cacheStrategy: config.CacheStrategy,
disableFallback: config.DisableFallback, disableFallback: config.DisableFallback,
disableFallbackIfMatch: config.DisableFallbackIfMatch,
checkSystem: checkSystem,
}, nil }, nil
} }
@@ -171,152 +145,146 @@ func (s *DNS) Close() error {
// IsOwnLink implements proxy.dns.ownLinkVerifier // IsOwnLink implements proxy.dns.ownLinkVerifier
func (s *DNS) IsOwnLink(ctx context.Context) bool { func (s *DNS) IsOwnLink(ctx context.Context) bool {
inbound := session.InboundFromContext(ctx) inbound := session.InboundFromContext(ctx)
if inbound == nil { return inbound != nil && inbound.Tag == s.tag
return false
}
for _, client := range s.clients {
if client.tag == inbound.Tag {
return true
}
}
return false
} }
// LookupIP implements dns.Client. // LookupIP implements dns.Client.
func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, error) { func (s *DNS) LookupIP(domain string) ([]net.IP, error) {
// Normalize the FQDN form query return s.lookupIPInternal(domain, s.ipOption.Copy())
domain = strings.TrimSuffix(domain, ".") }
// LookupOptions implements dns.Client.
func (s *DNS) LookupOptions(domain string, opts ...dns.Option) ([]net.IP, error) {
opt := s.ipOption.Copy()
for _, o := range opts {
if o != nil {
o(opt)
}
}
return s.lookupIPInternal(domain, opt)
}
// LookupIPv4 implements dns.IPv4Lookup.
func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) {
return s.lookupIPInternal(domain, &dns.IPOption{
IPv4Enable: true,
})
}
// LookupIPv6 implements dns.IPv6Lookup.
func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) {
return s.lookupIPInternal(domain, &dns.IPOption{
IPv6Enable: true,
})
}
func (s *DNS) lookupIPInternal(domain string, option *dns.IPOption) ([]net.IP, error) {
if domain == "" { if domain == "" {
return nil, 0, errors.New("empty domain name") return nil, newError("empty domain name")
}
if isQuery(option) {
return nil, newError("empty option: Impossible.").AtWarning()
} }
if s.checkSystem { // Normalize the FQDN form query
supportIPv4, supportIPv6 := checkSystemNetwork() if strings.HasSuffix(domain, ".") {
option.IPv4Enable = option.IPv4Enable && supportIPv4 domain = domain[:len(domain)-1]
option.IPv6Enable = option.IPv6Enable && supportIPv6
} else {
option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable
option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable
}
if !option.IPv4Enable && !option.IPv6Enable {
return nil, 0, dns.ErrEmptyResponse
} }
// Static host lookup // Static host lookup
switch addrs, err := s.hosts.Lookup(domain, option); { switch addrs := s.hosts.Lookup(domain, option); {
case err != nil:
if go_errors.Is(err, dns.ErrEmptyResponse) {
return nil, 0, dns.ErrEmptyResponse
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(err)
case addrs == nil: // Domain not recorded in static host case addrs == nil: // Domain not recorded in static host
break break
case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled) 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 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() domain = addrs[0].Domain()
default: // Successfully found ip records in static host default:
errors.LogInfo(s.ctx, "returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs) // Successfully found ip records in static host.
ips, err := toNetIP(addrs) // Skip hosts mapping result in FakeDNS query.
if err != nil { if isIPQuery(option) {
return nil, 0, err newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog()
return toNetIP(addrs)
} }
return ips, 10, nil // Hosts ttl is 10
} }
// Name servers lookup // Name servers lookup
var errs []error errs := []error{}
for _, client := range s.sortClients(domain) { ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag})
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { for _, client := range s.sortClients(domain, option) {
errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) ips, err := client.QueryIP(ctx, domain, *option, s.cacheStrategy)
continue
}
ips, ttl, err := client.QueryIP(s.ctx, domain, option)
if len(ips) > 0 { if len(ips) > 0 {
if ttl == 0 { return ips, nil
ttl = 1
}
return ips, ttl, nil
} }
if err != nil {
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name()) newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
if err == nil { errs = append(errs, err)
err = dns.ErrEmptyResponse
} }
errs = append(errs, err) if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
return nil, err
if client.IsFinalQuery() {
break
} }
} }
if len(errs) > 0 { return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...))
allErrs := errors.Combine(errs...)
err0 := errs[0]
if errors.AllEqual(err0, allErrs) {
if go_errors.Is(err0, dns.ErrEmptyResponse) {
return nil, 0, dns.ErrEmptyResponse
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(err0)
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(allErrs)
}
return nil, 0, dns.ErrEmptyResponse
} }
func (s *DNS) sortClients(domain string) []*Client { func (s *DNS) sortClients(domain string, option *dns.IPOption) []*Client {
clients := make([]*Client, 0, len(s.clients)) clients := make([]*Client, 0, len(s.clients))
clientUsed := make([]bool, len(s.clients)) clientUsed := make([]bool, len(s.clients))
clientNames := make([]string, 0, len(s.clients)) clientNames := make([]string, 0, len(s.clients))
domainRules := []string{} domainRules := []string{}
// Priority domain matching defer func() {
hasMatch := false if len(domainRules) > 0 {
MatchSlice := s.domainMatcher.Match(domain) newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog()
sort.Slice(MatchSlice, func(i, j int) bool {
return MatchSlice[i] < MatchSlice[j]
})
for _, match := range MatchSlice {
info := s.matcherInfos[match]
client := s.clients[info.clientIdx]
domainRule := client.domains[info.domainRuleIdx]
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
if clientUsed[info.clientIdx] {
continue
} }
clientUsed[info.clientIdx] = true if len(clientNames) > 0 {
clients = append(clients, client) newError("domain ", domain, " will use DNS in order: ", clientNames).AtDebug().WriteToLog()
clientNames = append(clientNames, client.Name()) }
hasMatch = true if len(clients) == 0 {
} clients = append(clients, s.clients[0])
clientNames = append(clientNames, s.clients[0].Name())
newError("domain ", domain, " will use the first DNS: ", clientNames).AtDebug().WriteToLog()
}
}()
if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) { // Priority domain matching
// Default round-robin query for clientIdx, client := range s.clients {
for idx, client := range s.clients { if ids := client.domainMatcher.Match(domain); len(ids) > 0 {
if clientUsed[idx] || client.skipFallback { if !canQueryOnClient(option, client) {
newError("skipping the client " + client.Name()).AtDebug().WriteToLog()
continue
}
for _, id := range ids {
rule := client.findRule(id)
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", rule, clientIdx))
}
if clientUsed[clientIdx] {
continue continue
} }
clientUsed[idx] = true
clients = append(clients, client) clients = append(clients, client)
clientNames = append(clientNames, client.Name()) clientNames = append(clientNames, client.Name())
} }
} }
if len(domainRules) > 0 { if !s.disableFallback {
errors.LogDebug(s.ctx, "domain ", domain, " matches following rules: ", domainRules) // Default round-robin query
} for idx, client := range s.clients {
if len(clientNames) > 0 { if clientUsed[idx] || client.skipFallback {
errors.LogDebug(s.ctx, "domain ", domain, " will use DNS in order: ", clientNames) continue
} }
if len(clients) == 0 { if !canQueryOnClient(option, client) {
clients = append(clients, s.clients[0]) newError("skipping the client " + client.Name()).AtDebug().WriteToLog()
clientNames = append(clientNames, s.clients[0].Name()) continue
errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames) }
clientUsed[idx] = true
clients = append(clients, client)
clientNames = append(clientNames, client.Name())
}
} }
return clients return clients
@@ -327,22 +295,3 @@ func init() {
return New(ctx, config.(*Config)) 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
}

View File

@@ -6,14 +6,15 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/xtls/xray-core/app/dispatcher" "github.com/xtls/xray-core/app/dispatcher"
. "github.com/xtls/xray-core/app/dns" . "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/app/policy" "github.com/xtls/xray-core/app/policy"
"github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/proxyman"
_ "github.com/xtls/xray-core/app/proxyman/outbound" _ "github.com/xtls/xray-core/app/proxyman/outbound"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/matcher/domain"
"github.com/xtls/xray-core/common/matcher/geoip"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
@@ -22,7 +23,8 @@ import (
"github.com/xtls/xray-core/testing/servers/udp" "github.com/xtls/xray-core/testing/servers/udp"
) )
type staticHandler struct{} type staticHandler struct {
}
func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
ans := new(dns.Msg) ans := new(dns.Msg)
@@ -76,9 +78,6 @@ func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
case q.Name == "notexist.google.com." && q.Qtype == dns.TypeAAAA: case q.Name == "notexist.google.com." && q.Qtype == dns.TypeAAAA:
ans.MsgHdr.Rcode = dns.RcodeNameError 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: case q.Name == "hostname." && q.Qtype == dns.TypeA:
rr, _ := dns.NewRR("hostname. IN A 127.0.0.1") rr, _ := dns.NewRR("hostname. IN A 127.0.0.1")
ans.Answer = append(ans.Answer, rr) ans.Answer = append(ans.Answer, rr)
@@ -120,23 +119,22 @@ func TestUDPServerSubnet(t *testing.T) {
Handler: &staticHandler{}, Handler: &staticHandler{},
UDPSize: 1200, UDPSize: 1200,
} }
go dnsServer.ListenAndServe() go dnsServer.ListenAndServe()
time.Sleep(time.Second) time.Sleep(time.Second)
config := &core.Config{ config := &core.Config{
App: []*serial.TypedMessage{ App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{ serial.ToTypedMessage(&Config{
NameServer: []*NameServer{ NameServers: []*net.Endpoint{
{ {
Address: &net.Endpoint{ Network: net.Network_UDP,
Network: net.Network_UDP, Address: &net.IPOrDomain{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{
Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1},
Ip: []byte{127, 0, 0, 1},
},
}, },
Port: uint32(port),
}, },
Port: uint32(port),
}, },
}, },
ClientIp: []byte{7, 8, 9, 10}, ClientIp: []byte{7, 8, 9, 10},
@@ -157,11 +155,7 @@ func TestUDPServerSubnet(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ ips, err := client.LookupIP("google.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -187,17 +181,15 @@ func TestUDPServer(t *testing.T) {
config := &core.Config{ config := &core.Config{
App: []*serial.TypedMessage{ App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{ serial.ToTypedMessage(&Config{
NameServer: []*NameServer{ NameServers: []*net.Endpoint{
{ {
Address: &net.Endpoint{ Network: net.Network_UDP,
Network: net.Network_UDP, Address: &net.IPOrDomain{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{
Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1},
Ip: []byte{127, 0, 0, 1},
},
}, },
Port: uint32(port),
}, },
Port: uint32(port),
}, },
}, },
}), }),
@@ -218,11 +210,7 @@ func TestUDPServer(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
{ {
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ ips, err := client.LookupIP("google.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -233,11 +221,7 @@ func TestUDPServer(t *testing.T) {
} }
{ {
ips, _, err := client.LookupIP("facebook.com", feature_dns.IPOption{ ips, err := client.LookupIP("facebook.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -248,11 +232,7 @@ func TestUDPServer(t *testing.T) {
} }
{ {
_, _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{ _, err := client.LookupIP("notexist.google.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err == nil { if err == nil {
t.Fatal("nil error") t.Fatal("nil error")
} }
@@ -262,12 +242,9 @@ func TestUDPServer(t *testing.T) {
} }
{ {
ips, _, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{ clientv6 := client.(feature_dns.IPv6Lookup)
IPv4Enable: false, ips, err := clientv6.LookupIPv6("ipv4only.google.com")
IPv6Enable: true, if err != feature_dns.ErrEmptyResponse {
FakeEnable: false,
})
if !errors.AllEqual(feature_dns.ErrEmptyResponse, errors.Cause(err)) {
t.Fatal("error: ", err) t.Fatal("error: ", err)
} }
if len(ips) != 0 { if len(ips) != 0 {
@@ -278,11 +255,7 @@ func TestUDPServer(t *testing.T) {
dnsServer.Shutdown() dnsServer.Shutdown()
{ {
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ ips, err := client.LookupIP("google.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -309,18 +282,18 @@ func TestPrioritizedDomain(t *testing.T) {
config := &core.Config{ config := &core.Config{
App: []*serial.TypedMessage{ App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{ serial.ToTypedMessage(&Config{
NameServer: []*NameServer{ NameServers: []*net.Endpoint{
{ {
Address: &net.Endpoint{ Network: net.Network_UDP,
Network: net.Network_UDP, Address: &net.IPOrDomain{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{
Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1},
Ip: []byte{127, 0, 0, 1},
},
}, },
Port: 9999, /* unreachable */
}, },
Port: 9999, /* unreachable */
}, },
},
NameServer: []*NameServer{
{ {
Address: &net.Endpoint{ Address: &net.Endpoint{
Network: net.Network_UDP, Network: net.Network_UDP,
@@ -331,10 +304,10 @@ func TestPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
PrioritizedDomain: []*NameServer_PriorityDomain{ PrioritizedDomain: []*domain.Domain{
{ {
Type: DomainMatchingType_Full, Type: domain.MatchingType_Full,
Domain: "google.com", Value: "google.com",
}, },
}, },
}, },
@@ -359,11 +332,7 @@ func TestPrioritizedDomain(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ {
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ ips, err := client.LookupIP("google.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -395,17 +364,15 @@ func TestUDPServerIPv6(t *testing.T) {
config := &core.Config{ config := &core.Config{
App: []*serial.TypedMessage{ App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{ serial.ToTypedMessage(&Config{
NameServer: []*NameServer{ NameServers: []*net.Endpoint{
{ {
Address: &net.Endpoint{ Network: net.Network_UDP,
Network: net.Network_UDP, Address: &net.IPOrDomain{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{
Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1},
Ip: []byte{127, 0, 0, 1},
},
}, },
Port: uint32(port),
}, },
Port: uint32(port),
}, },
}, },
}), }),
@@ -424,12 +391,9 @@ func TestUDPServerIPv6(t *testing.T) {
common.Must(err) common.Must(err)
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
client6 := client.(feature_dns.IPv6Lookup)
{ {
ips, _, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ ips, err := client6.LookupIPv6("ipv6.google.com")
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -456,22 +420,20 @@ func TestStaticHostDomain(t *testing.T) {
config := &core.Config{ config := &core.Config{
App: []*serial.TypedMessage{ App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{ serial.ToTypedMessage(&Config{
NameServer: []*NameServer{ NameServers: []*net.Endpoint{
{ {
Address: &net.Endpoint{ Network: net.Network_UDP,
Network: net.Network_UDP, Address: &net.IPOrDomain{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{
Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1},
Ip: []byte{127, 0, 0, 1},
},
}, },
Port: uint32(port),
}, },
Port: uint32(port),
}, },
}, },
StaticHosts: []*Config_HostMapping{ StaticHosts: []*Config_HostMapping{
{ {
Type: DomainMatchingType_Full, Type: domain.MatchingType_Full,
Domain: "example.com", Domain: "example.com",
ProxiedDomain: "google.com", ProxiedDomain: "google.com",
}, },
@@ -494,11 +456,7 @@ func TestStaticHostDomain(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
{ {
ips, _, err := client.LookupIP("example.com", feature_dns.IPOption{ ips, err := client.LookupIP("example.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -539,10 +497,10 @@ func TestIPMatch(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
ExpectedGeoip: []*router.GeoIP{ Geoip: []*geoip.GeoIP{
{ {
CountryCode: "local", CountryCode: "local",
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{ {
// inner ip, will not match // inner ip, will not match
Ip: []byte{192, 168, 11, 1}, Ip: []byte{192, 168, 11, 1},
@@ -563,10 +521,10 @@ func TestIPMatch(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
ExpectedGeoip: []*router.GeoIP{ Geoip: []*geoip.GeoIP{
{ {
CountryCode: "test", CountryCode: "test",
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{ {
Ip: []byte{8, 8, 8, 8}, Ip: []byte{8, 8, 8, 8},
Prefix: 32, Prefix: 32,
@@ -575,7 +533,7 @@ func TestIPMatch(t *testing.T) {
}, },
{ {
CountryCode: "test", CountryCode: "test",
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{ {
Ip: []byte{8, 8, 8, 4}, Ip: []byte{8, 8, 8, 4},
Prefix: 32, Prefix: 32,
@@ -605,11 +563,7 @@ func TestIPMatch(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ {
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ ips, err := client.LookupIP("google.com")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -641,18 +595,18 @@ func TestLocalDomain(t *testing.T) {
config := &core.Config{ config := &core.Config{
App: []*serial.TypedMessage{ App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{ serial.ToTypedMessage(&Config{
NameServer: []*NameServer{ NameServers: []*net.Endpoint{
{ {
Address: &net.Endpoint{ Network: net.Network_UDP,
Network: net.Network_UDP, Address: &net.IPOrDomain{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{
Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1},
Ip: []byte{127, 0, 0, 1},
},
}, },
Port: 9999, /* unreachable */
}, },
Port: 9999, /* unreachable */
}, },
},
NameServer: []*NameServer{
{ {
Address: &net.Endpoint{ Address: &net.Endpoint{
Network: net.Network_UDP, Network: net.Network_UDP,
@@ -663,14 +617,14 @@ func TestLocalDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
PrioritizedDomain: []*NameServer_PriorityDomain{ PrioritizedDomain: []*domain.Domain{
// Equivalent of dotless:localhost // Equivalent of dotless:localhost
{Type: DomainMatchingType_Regex, Domain: "^[^.]*localhost[^.]*$"}, {Type: domain.MatchingType_Regex, Value: "^[^.]*localhost[^.]*$"},
}, },
ExpectedGeoip: []*router.GeoIP{ Geoip: []*geoip.GeoIP{
{ // Will match localhost, localhost-a and localhost-b, { // Will match localhost, localhost-a and localhost-b,
CountryCode: "local", CountryCode: "local",
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{Ip: []byte{127, 0, 0, 2}, Prefix: 32}, {Ip: []byte{127, 0, 0, 2}, Prefix: 32},
{Ip: []byte{127, 0, 0, 3}, Prefix: 32}, {Ip: []byte{127, 0, 0, 3}, Prefix: 32},
{Ip: []byte{127, 0, 0, 4}, Prefix: 32}, {Ip: []byte{127, 0, 0, 4}, Prefix: 32},
@@ -688,22 +642,22 @@ func TestLocalDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
PrioritizedDomain: []*NameServer_PriorityDomain{ PrioritizedDomain: []*domain.Domain{
// Equivalent of dotless: and domain:local // Equivalent of dotless: and domain:local
{Type: DomainMatchingType_Regex, Domain: "^[^.]*$"}, {Type: domain.MatchingType_Regex, Value: "^[^.]*$"},
{Type: DomainMatchingType_Subdomain, Domain: "local"}, {Type: domain.MatchingType_Subdomain, Value: "local"},
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"}, {Type: domain.MatchingType_Subdomain, Value: "localdomain"},
}, },
}, },
}, },
StaticHosts: []*Config_HostMapping{ StaticHosts: []*Config_HostMapping{
{ {
Type: DomainMatchingType_Full, Type: domain.MatchingType_Full,
Domain: "hostnamestatic", Domain: "hostnamestatic",
Ip: [][]byte{{127, 0, 0, 53}}, Ip: [][]byte{{127, 0, 0, 53}},
}, },
{ {
Type: DomainMatchingType_Full, Type: domain.MatchingType_Full,
Domain: "hostnamealias", Domain: "hostnamealias",
ProxiedDomain: "hostname.localdomain", ProxiedDomain: "hostname.localdomain",
}, },
@@ -728,11 +682,7 @@ func TestLocalDomain(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ // Will match dotless: { // Will match dotless:
ips, _, err := client.LookupIP("hostname", feature_dns.IPOption{ ips, err := client.LookupIP("hostname")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -743,11 +693,7 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match domain:local { // Will match domain:local
ips, _, err := client.LookupIP("hostname.local", feature_dns.IPOption{ ips, err := client.LookupIP("hostname.local")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -758,11 +704,7 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match static ip { // Will match static ip
ips, _, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{ ips, err := client.LookupIP("hostnamestatic")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -773,11 +715,7 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match domain replacing { // Will match domain replacing
ips, _, err := client.LookupIP("hostnamealias", feature_dns.IPOption{ ips, err := client.LookupIP("hostnamealias")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -787,12 +725,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: { // 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{ ips, err := client.LookupIP("localhost")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -802,12 +736,8 @@ func TestLocalDomain(t *testing.T) {
} }
} }
{ // Will match dotless:localhost, and expectedIPs: 127.0.0.2, 127.0.0.3 { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
ips, _, err := client.LookupIP("localhost-a", feature_dns.IPOption{ ips, err := client.LookupIP("localhost-a")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -817,12 +747,8 @@ func TestLocalDomain(t *testing.T) {
} }
} }
{ // Will match dotless:localhost, and expectedIPs: 127.0.0.2, 127.0.0.3 { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
ips, _, err := client.LookupIP("localhost-b", feature_dns.IPOption{ ips, err := client.LookupIP("localhost-b")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -833,11 +759,7 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match dotless: { // Will match dotless:
ips, _, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{ ips, err := client.LookupIP("Mijia Cloud")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -869,18 +791,18 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
config := &core.Config{ config := &core.Config{
App: []*serial.TypedMessage{ App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{ serial.ToTypedMessage(&Config{
NameServer: []*NameServer{ NameServers: []*net.Endpoint{
{ {
Address: &net.Endpoint{ Network: net.Network_UDP,
Network: net.Network_UDP, Address: &net.IPOrDomain{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{
Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1},
Ip: []byte{127, 0, 0, 1},
},
}, },
Port: 9999, /* unreachable */
}, },
Port: 9999, /* unreachable */
}, },
},
NameServer: []*NameServer{
{ {
Address: &net.Endpoint{ Address: &net.Endpoint{
Network: net.Network_UDP, Network: net.Network_UDP,
@@ -891,15 +813,15 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
PrioritizedDomain: []*NameServer_PriorityDomain{ PrioritizedDomain: []*domain.Domain{
{ {
Type: DomainMatchingType_Subdomain, Type: domain.MatchingType_Subdomain,
Domain: "google.com", Value: "google.com",
}, },
}, },
ExpectedGeoip: []*router.GeoIP{ Geoip: []*geoip.GeoIP{
{ // Will only match 8.8.8.8 and 8.8.4.4 { // Will only match 8.8.8.8 and 8.8.4.4
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{Ip: []byte{8, 8, 8, 8}, Prefix: 32}, {Ip: []byte{8, 8, 8, 8}, Prefix: 32},
{Ip: []byte{8, 8, 4, 4}, Prefix: 32}, {Ip: []byte{8, 8, 4, 4}, Prefix: 32},
}, },
@@ -916,15 +838,15 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
PrioritizedDomain: []*NameServer_PriorityDomain{ PrioritizedDomain: []*domain.Domain{
{ {
Type: DomainMatchingType_Subdomain, Type: domain.MatchingType_Subdomain,
Domain: "google.com", Value: "google.com",
}, },
}, },
ExpectedGeoip: []*router.GeoIP{ Geoip: []*geoip.GeoIP{
{ // Will match 8.8.8.8 and 8.8.8.7, etc { // Will match 8.8.8.8 and 8.8.8.7, etc
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{Ip: []byte{8, 8, 8, 7}, Prefix: 24}, {Ip: []byte{8, 8, 8, 7}, Prefix: 24},
}, },
}, },
@@ -940,15 +862,15 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
PrioritizedDomain: []*NameServer_PriorityDomain{ PrioritizedDomain: []*domain.Domain{
{ {
Type: DomainMatchingType_Subdomain, Type: domain.MatchingType_Subdomain,
Domain: "api.google.com", Value: "api.google.com",
}, },
}, },
ExpectedGeoip: []*router.GeoIP{ Geoip: []*geoip.GeoIP{
{ // Will only match 8.8.7.7 (api.google.com) { // Will only match 8.8.7.7 (api.google.com)
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{Ip: []byte{8, 8, 7, 7}, Prefix: 32}, {Ip: []byte{8, 8, 7, 7}, Prefix: 32},
}, },
}, },
@@ -964,15 +886,15 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
PrioritizedDomain: []*NameServer_PriorityDomain{ PrioritizedDomain: []*domain.Domain{
{ {
Type: DomainMatchingType_Full, Type: domain.MatchingType_Full,
Domain: "v2.api.google.com", Value: "v2.api.google.com",
}, },
}, },
ExpectedGeoip: []*router.GeoIP{ Geoip: []*geoip.GeoIP{
{ // Will only match 8.8.7.8 (v2.api.google.com) { // Will only match 8.8.7.8 (v2.api.google.com)
Cidr: []*router.CIDR{ Cidr: []*geoip.CIDR{
{Ip: []byte{8, 8, 7, 8}, Prefix: 32}, {Ip: []byte{8, 8, 7, 8}, Prefix: 32},
}, },
}, },
@@ -999,11 +921,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ // Will match server 1,2 and server 1 returns expected ip { // 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")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -1014,11 +932,8 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
} }
{ // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one { // 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{ clientv4 := client.(feature_dns.IPv4Lookup)
IPv4Enable: true, ips, err := clientv4.LookupIPv4("ipv6.google.com")
IPv6Enable: false,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -1029,11 +944,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
} }
{ // Will match server 3,1,2 and server 3 returns expected one { // 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")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@@ -1044,11 +955,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
} }
{ // Will match server 4,3,1,2 and server 4 returns expected one { // 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")
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }

View File

@@ -1,22 +1,18 @@
package dns package dns
import ( import (
"context"
"encoding/binary" "encoding/binary"
"strings" "strings"
"time" "time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core"
dns_feature "github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
"golang.org/x/net/dns/dnsmessage" "golang.org/x/net/dns/dnsmessage"
) )
// Fqdn normalizes domain make sure it ends with '.' // Fqdn normalize domain make sure it ends with '.'
func Fqdn(domain string) string { func Fqdn(domain string) string {
if len(domain) > 0 && strings.HasSuffix(domain, ".") { if len(domain) > 0 && strings.HasSuffix(domain, ".") {
return domain return domain
@@ -31,34 +27,35 @@ type record struct {
// IPRecord is a cacheable item for a resolved domain // IPRecord is a cacheable item for a resolved domain
type IPRecord struct { type IPRecord struct {
ReqID uint16 ReqID uint16
IP []net.IP IP []net.Address
Expire time.Time Expire time.Time
RCode dnsmessage.RCode RCode dnsmessage.RCode
RawHeader *dnsmessage.Header
} }
func (r *IPRecord) getIPs() ([]net.IP, uint32, error) { func (r *IPRecord) getIPs() ([]net.Address, error) {
if r == nil { if r == nil || r.Expire.Before(time.Now()) {
return nil, 0, errRecordNotFound 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 { if r.RCode != dnsmessage.RCodeSuccess {
return nil, ttl, dns_feature.RCodeError(r.RCode) return nil, dns_feature.RCodeError(r.RCode)
} }
if len(r.IP) == 0 { return r.IP, nil
return nil, ttl, dns_feature.ErrEmptyResponse
}
return r.IP, ttl, nil
} }
var errRecordNotFound = errors.New("record not found") 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")
)
type dnsRequest struct { type dnsRequest struct {
reqType dnsmessage.Type reqType dnsmessage.Type
@@ -68,59 +65,49 @@ type dnsRequest struct {
msg *dnsmessage.Message msg *dnsmessage.Message
} }
func genEDNS0Options(clientIP net.IP, padding int) *dnsmessage.Resource { func genEDNS0Options(clientIP net.IP) *dnsmessage.Resource {
if len(clientIP) == 0 && padding == 0 { if len(clientIP) == 0 {
return nil return nil
} }
const EDNS0SUBNET = 0x8 var netmask int
const EDNS0PADDING = 0xc 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) opt := new(dnsmessage.Resource)
common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true)) common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true))
body := dnsmessage.OPTResource{}
opt.Body = &body
if len(clientIP) != 0 { opt.Body = &dnsmessage.OPTResource{
var netmask int Options: []dnsmessage.Option{
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{
Code: EDNS0SUBNET, Code: EDNS0SUBNET,
Data: b, Data: b,
}) },
} },
if padding != 0 {
body.Options = append(body.Options,
dnsmessage.Option{
Code: EDNS0PADDING,
Data: make([]byte, padding),
})
} }
return opt return opt
@@ -177,23 +164,22 @@ func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() ui
return reqs return reqs
} }
// parseResponse parses DNS answers from the returned payload // parseResponse parse DNS answers from the returned payload
func parseResponse(payload []byte) (*IPRecord, error) { func parseResponse(payload []byte) (*IPRecord, error) {
var parser dnsmessage.Parser var parser dnsmessage.Parser
h, err := parser.Start(payload) h, err := parser.Start(payload)
if err != nil { 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 { 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() now := time.Now()
ipRecord := &IPRecord{ ipRecord := &IPRecord{
ReqID: h.ID, ReqID: h.ID,
RCode: h.RCode, RCode: h.RCode,
Expire: now.Add(time.Second * dns_feature.DefaultTTL), Expire: now.Add(time.Second * 600),
RawHeader: &h,
} }
L: L:
@@ -201,14 +187,14 @@ L:
ah, err := parser.AnswerHeader() ah, err := parser.AnswerHeader()
if err != nil { if err != nil {
if err != dnsmessage.ErrSectionDone { 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 break
} }
ttl := ah.TTL ttl := ah.TTL
if ttl == 0 { if ttl == 0 {
ttl = 1 ttl = 600
} }
expire := now.Add(time.Duration(ttl) * time.Second) expire := now.Add(time.Duration(ttl) * time.Second)
if ipRecord.Expire.After(expire) { if ipRecord.Expire.After(expire) {
@@ -219,23 +205,20 @@ L:
case dnsmessage.TypeA: case dnsmessage.TypeA:
ans, err := parser.AResource() ans, err := parser.AResource()
if err != nil { 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 break L
} }
ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.A[:]).IP()) ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.A[:]))
case dnsmessage.TypeAAAA: case dnsmessage.TypeAAAA:
ans, err := parser.AAAAResource() ans, err := parser.AAAAResource()
if err != nil { if err != nil {
errors.LogInfoInner(context.Background(), err, "failed to parse AAAA record for domain: ", ah.Name) newError("failed to parse A record for domain: ", ah.Name).Base(err).WriteToLog()
break L break L
} }
newIP := net.IPAddress(ans.AAAA[:]).IP() ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.AAAA[:]))
if len(newIP) == net.IPv6len {
ipRecord.IP = append(ipRecord.IP, newIP)
}
default: default:
if err := parser.SkipAnswer(); err != nil { 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 break L
} }
continue continue
@@ -244,19 +227,3 @@ L:
return ipRecord, nil return ipRecord, nil
} }
// toDnsContext create a new background context with parent inbound, session and dns log
func toDnsContext(ctx context.Context, addr string) context.Context {
dnsCtx := core.ToBackgroundDetachedContext(ctx)
if inbound := session.InboundFromContext(ctx); inbound != nil {
dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
}
dnsCtx = session.ContextWithContent(dnsCtx, session.ContentFromContext(ctx))
dnsCtx = log.ContextWithAccessMessage(dnsCtx, &log.AccessMessage{
From: "DNS",
To: addr,
Status: log.AccessAccepted,
Reason: "",
})
return dnsCtx
}

View File

@@ -49,30 +49,21 @@ func Test_parseResponse(t *testing.T) {
want *IPRecord want *IPRecord
wantErr bool wantErr bool
}{ }{
{ {"empty",
"empty", &IPRecord{0, []net.Address(nil), time.Time{}, dnsmessage.RCodeSuccess},
&IPRecord{0, []net.IP(nil), time.Time{}, dnsmessage.RCodeSuccess, nil},
false, false,
}, },
{ {"error",
"error",
nil, nil,
true, true,
}, },
{ {"a record",
"a record", &IPRecord{1, []net.Address{net.ParseAddress("8.8.8.8"), net.ParseAddress("8.8.4.4")},
&IPRecord{ time.Time{}, dnsmessage.RCodeSuccess},
1,
[]net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")},
time.Time{},
dnsmessage.RCodeSuccess,
nil,
},
false, false,
}, },
{ {"aaaa record",
"aaaa record", &IPRecord{2, []net.Address{net.ParseAddress("2001::123:8888"), net.ParseAddress("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess},
&IPRecord{2, []net.IP{net.ParseIP("2001::123:8888"), net.ParseIP("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess, nil},
false, false,
}, },
} }
@@ -85,12 +76,11 @@ func Test_parseResponse(t *testing.T) {
} }
if got != nil { if got != nil {
// reset the time and RawHeader // reset the time
got.Expire = time.Time{} got.Expire = time.Time{}
got.RawHeader = nil
} }
if cmp.Diff(got, tt.want) != "" { 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) // t.Errorf("handleResponse() = %#v, want %#v", got, tt.want)
} }
}) })
@@ -156,7 +146,7 @@ func Test_genEDNS0Options(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := genEDNS0Options(tt.args.clientIP, 0); got == nil { if got := genEDNS0Options(tt.args.clientIP); got == nil {
t.Errorf("genEDNS0Options() = %v, want %v", got, tt.want) t.Errorf("genEDNS0Options() = %v, want %v", got, tt.want)
} }
}) })

View File

@@ -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{})
}

View File

@@ -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{})
}

View File

@@ -5,12 +5,10 @@ import (
"math" "math"
"math/big" "math/big"
gonet "net" gonet "net"
"sync"
"time" "time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/cache" "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/common/net"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
@@ -18,41 +16,21 @@ import (
type Holder struct { type Holder struct {
domainToIP cache.Lru domainToIP cache.Lru
ipRange *gonet.IPNet ipRange *gonet.IPNet
mu *sync.Mutex
config *FakeDnsPool config *FakeDnsPool
} }
func (fkdns *Holder) IsIPInIPPool(ip net.Address) bool {
if ip.Family().IsDomain() {
return false
}
return fkdns.ipRange.Contains(ip.IP())
}
func (fkdns *Holder) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {
isIPv6 := fkdns.ipRange.IP.To4() == nil
if (isIPv6 && ipv6) || (!isIPv6 && ipv4) {
return fkdns.GetFakeIPForDomain(domain)
}
return []net.Address{}
}
func (*Holder) Type() interface{} { func (*Holder) Type() interface{} {
return (*dns.FakeDNSEngine)(nil) return (*dns.FakeDNSEngine)(nil)
} }
func (fkdns *Holder) Start() error { func (fkdns *Holder) Start() error {
if fkdns.config != nil && fkdns.config.IpPool != "" && fkdns.config.LruSize != 0 { return fkdns.initializeFromConfig()
return fkdns.initializeFromConfig()
}
return errors.New("invalid fakeDNS setting")
} }
func (fkdns *Holder) Close() error { func (fkdns *Holder) Close() error {
fkdns.domainToIP = nil fkdns.domainToIP = nil
fkdns.ipRange = nil fkdns.ipRange = nil
fkdns.mu = nil
return nil return nil
} }
@@ -61,9 +39,9 @@ func NewFakeDNSHolder() (*Holder, error) {
var err error var err error
if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil { 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) err = fkdns.initialize(dns.FakeIPPool, 65535)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -71,7 +49,7 @@ func NewFakeDNSHolder() (*Holder, error) {
} }
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) { func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) {
return &Holder{nil, nil, nil, conf}, nil return &Holder{nil, nil, conf}, nil
} }
func (fkdns *Holder) initializeFromConfig() error { func (fkdns *Holder) initializeFromConfig() error {
@@ -83,34 +61,31 @@ func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
var err error var err error
if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil { if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError() return newError("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
} }
ones, bits := ipRange.Mask.Size() ones, bits := ipRange.Mask.Size()
rooms := bits - ones rooms := bits - ones
if math.Log2(float64(lruSize)) >= float64(rooms) { 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.domainToIP = cache.NewLru(lruSize)
fkdns.ipRange = ipRange fkdns.ipRange = ipRange
fkdns.mu = new(sync.Mutex)
return nil return nil
} }
// GetFakeIPForDomain checks and generates a fake IP for a domain name // GetFakeIPForDomain check and generate a fake IP for a domain name
func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address { func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
fkdns.mu.Lock()
defer fkdns.mu.Unlock()
if v, ok := fkdns.domainToIP.Get(domain); ok { if v, ok := fkdns.domainToIP.Get(domain); ok {
return []net.Address{v.(net.Address)} return []net.Address{v.(net.Address)}
} }
currentTimeMillis := uint64(time.Now().UnixNano() / 1e6) var currentTimeMillis = uint64(time.Now().UnixNano() / 1e6)
ones, bits := fkdns.ipRange.Mask.Size() ones, bits := fkdns.ipRange.Mask.Size()
rooms := bits - ones rooms := bits - ones
if rooms < 64 { if rooms < 64 {
currentTimeMillis %= (uint64(1) << rooms) currentTimeMillis %= (uint64(1) << rooms)
} }
bigIntIP := big.NewInt(0).SetBytes(fkdns.ipRange.IP) var bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP)
bigIntIP = bigIntIP.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis)) bigIntIP = bigIntIP.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis))
var ip net.Address var ip net.Address
for { for {
@@ -130,7 +105,7 @@ func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
return []net.Address{ip} return []net.Address{ip}
} }
// GetDomainFromFakeDNS checks if an IP is a fake IP and have corresponding domain name // GetDomainFromFakeDNS check if an IP is a fake IP and have corresponding domain name
func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string { func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
if !ip.Family().IsIP() || !fkdns.ipRange.Contains(ip.IP()) { if !ip.Family().IsIP() || !fkdns.ipRange.Contains(ip.IP()) {
return "" return ""
@@ -138,96 +113,13 @@ func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok { if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok {
return k.(string) 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 "" return ""
} }
type HolderMulti struct { // GetFakeIPRange return fake IP range from configuration
holders []*Holder func (fkdns *Holder) GetFakeIPRange() *gonet.IPNet {
return fkdns.ipRange
config *FakeDnsPoolMulti
}
func (h *HolderMulti) IsIPInIPPool(ip net.Address) bool {
if ip.Family().IsDomain() {
return false
}
for _, v := range h.holders {
if v.IsIPInIPPool(ip) {
return true
}
}
return false
}
func (h *HolderMulti) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {
var ret []net.Address
for _, v := range h.holders {
ret = append(ret, v.GetFakeIPForDomain3(domain, ipv4, ipv6)...)
}
return ret
}
func (h *HolderMulti) GetFakeIPForDomain(domain string) []net.Address {
var ret []net.Address
for _, v := range h.holders {
ret = append(ret, v.GetFakeIPForDomain(domain)...)
}
return ret
}
func (h *HolderMulti) GetDomainFromFakeDNS(ip net.Address) string {
for _, v := range h.holders {
if domain := v.GetDomainFromFakeDNS(ip); domain != "" {
return domain
}
}
return ""
}
func (h *HolderMulti) Type() interface{} {
return (*dns.FakeDNSEngine)(nil)
}
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)
}
} else {
return errors.New("invalid fakeDNS setting")
}
}
return nil
}
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 nil
}
func (h *HolderMulti) createHolderGroups() error {
for _, v := range h.config.Pools {
holder, err := NewFakeDNSHolderConfigOnly(v)
if err != nil {
return err
}
h.holders = append(h.holders, holder)
}
return nil
}
func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) {
holderMulti := &HolderMulti{nil, conf}
if err := holderMulti.createHolderGroups(); err != nil {
return nil, err
}
return holderMulti, nil
} }
func init() { func init() {
@@ -239,13 +131,4 @@ func init() {
} }
return f, nil return f, nil
})) }))
common.Must(common.RegisterConfig((*FakeDnsPoolMulti)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
var f *HolderMulti
var err error
if f, err = NewFakeDNSHolderMulti(config.(*FakeDnsPoolMulti)); err != nil {
return nil, err
}
return f, nil
}))
} }

View File

@@ -1 +1,3 @@
package fakedns package fakedns
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen

View File

@@ -1,12 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.25.0
// protoc v5.28.2 // protoc v3.13.0
// source: app/dns/fakedns/fakedns.proto // source: app/dns/fakedns/fakedns.proto
package fakedns package fakedns
import ( import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
@@ -20,6 +21,10 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type FakeDnsPool struct { type FakeDnsPool struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -31,9 +36,11 @@ type FakeDnsPool struct {
func (x *FakeDnsPool) Reset() { func (x *FakeDnsPool) Reset() {
*x = FakeDnsPool{} *x = FakeDnsPool{}
mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *FakeDnsPool) String() string { func (x *FakeDnsPool) String() string {
@@ -44,7 +51,7 @@ func (*FakeDnsPool) ProtoMessage() {}
func (x *FakeDnsPool) ProtoReflect() protoreflect.Message { func (x *FakeDnsPool) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0] mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -73,73 +80,24 @@ func (x *FakeDnsPool) GetLruSize() int64 {
return 0 return 0
} }
type FakeDnsPoolMulti struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"`
}
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)
}
func (x *FakeDnsPoolMulti) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*FakeDnsPoolMulti) ProtoMessage() {}
func (x *FakeDnsPoolMulti) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_fakedns_fakedns_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 FakeDnsPoolMulti.ProtoReflect.Descriptor instead.
func (*FakeDnsPoolMulti) Descriptor() ([]byte, []int) {
return file_app_dns_fakedns_fakedns_proto_rawDescGZIP(), []int{1}
}
func (x *FakeDnsPoolMulti) GetPools() []*FakeDnsPool {
if x != nil {
return x.Pools
}
return nil
}
var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{ var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x14, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x1a, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46,
0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50,
0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02,
0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x4b, 0x0a, 0x10, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x5f, 0x0a,
0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x12, 0x37, 0x0a, 0x05, 0x70, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x50,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x01, 0x5a, 0x1e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72,
0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x05, 0x70, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
0x6f, 0x6f, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x73, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x62, 0x06,
0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x14,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b,
0x65, 0x64, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -154,18 +112,16 @@ func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte {
return file_app_dns_fakedns_fakedns_proto_rawDescData return file_app_dns_fakedns_fakedns_proto_rawDescData
} }
var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
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 (*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool
(*FakeDnsPoolMulti)(nil), // 1: xray.app.dns.fakedns.FakeDnsPoolMulti
} }
var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{ var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{
0, // 0: xray.app.dns.fakedns.FakeDnsPoolMulti.pools:type_name -> xray.app.dns.fakedns.FakeDnsPool 0, // [0:0] is the sub-list for method output_type
1, // [1:1] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type
1, // [1:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee
1, // [1:1] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name
0, // [0:1] is the sub-list for field type_name
} }
func init() { file_app_dns_fakedns_fakedns_proto_init() } func init() { file_app_dns_fakedns_fakedns_proto_init() }
@@ -173,13 +129,27 @@ func file_app_dns_fakedns_fakedns_proto_init() {
if File_app_dns_fakedns_fakedns_proto != nil { if File_app_dns_fakedns_fakedns_proto != nil {
return 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
}
}
}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc, RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 2, NumMessages: 1,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@@ -10,7 +10,3 @@ message FakeDnsPool{
string ip_pool = 1; //CIDR of IP pool used as fake DNS IP string ip_pool = 1; //CIDR of IP pool used as fake DNS IP
int64 lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address int64 lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address
} }
message FakeDnsPoolMulti{
repeated FakeDnsPool pools = 1;
}

View File

@@ -1,19 +1,19 @@
package fakedns package fakedns
import ( import (
gonet "net"
"strconv"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
"golang.org/x/sync/errgroup"
) )
var ipPrefix = "198.1" var (
ipPrefix = "198.18."
)
func TestNewFakeDnsHolder(_ *testing.T) { func TestNewFakeDnsHolder(_ *testing.T) {
_, err := NewFakeDNSHolder() _, err := NewFakeDNSHolder()
@@ -67,34 +67,9 @@ func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) {
assert.Equal(t, addr[0].IP().String(), addr2[0].IP().String()) assert.Equal(t, addr[0].IP().String(), addr2[0].IP().String())
} }
func TestGetFakeIPForDomainConcurrently(t *testing.T) {
fkdns, err := NewFakeDNSHolder()
common.Must(err)
total := 200
addr := make([][]net.Address, total)
var errg errgroup.Group
for i := 0; i < total; i++ {
errg.Go(testGetFakeIP(i, addr, fkdns))
}
errg.Wait()
for i := 0; i < total; i++ {
for j := i + 1; j < total; j++ {
assert.NotEqual(t, addr[i][0].IP().String(), addr[j][0].IP().String())
}
}
}
func testGetFakeIP(index int, addr [][]net.Address, fkdns *Holder) func() error {
return func() error {
addr[index] = fkdns.GetFakeIPForDomain("fakednstest" + strconv.Itoa(index) + ".example.com")
return nil
}
}
func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) { func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{ fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{
IpPool: dns.FakeIPv4Pool, IpPool: dns.FakeIPPool,
LruSize: 256, LruSize: 256,
}) })
common.Must(err) common.Must(err)
@@ -128,79 +103,3 @@ func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
} }
} }
} }
func TestFakeDNSMulti(t *testing.T) {
fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{
Pools: []*FakeDnsPool{{
IpPool: "240.0.0.0/12",
LruSize: 256,
}, {
IpPool: "fddd:c5b4:ff5f:f4f0::/64",
LruSize: 256,
}},
},
)
common.Must(err)
err = fakeMulti.Start()
common.Must(err)
assert.Nil(t, err, "Should not throw error")
_ = fakeMulti
t.Run("checkInRange", func(t *testing.T) {
t.Run("ipv4", func(t *testing.T) {
inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{240, 0, 0, 5}))
assert.True(t, inPool)
})
t.Run("ipv6", func(t *testing.T) {
ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.True(t, inPool)
})
t.Run("ipv4_inverse", func(t *testing.T) {
inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{241, 0, 0, 5}))
assert.False(t, inPool)
})
t.Run("ipv6_inverse", func(t *testing.T) {
ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.False(t, inPool)
})
})
t.Run("allocateTwoAddressForTwoPool", func(t *testing.T) {
address := fakeMulti.GetFakeIPForDomain("fakednstest.example.com")
assert.Len(t, address, 2, "should be 2 address one for each pool")
t.Run("eachOfThemShouldResolve:0", func(t *testing.T) {
domain := fakeMulti.GetDomainFromFakeDNS(address[0])
assert.Equal(t, "fakednstest.example.com", domain)
})
t.Run("eachOfThemShouldResolve:1", func(t *testing.T) {
domain := fakeMulti.GetDomainFromFakeDNS(address[1])
assert.Equal(t, "fakednstest.example.com", domain)
})
})
t.Run("understandIPTypeSelector", func(t *testing.T) {
t.Run("ipv4", func(t *testing.T) {
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv4.example.com", true, false)
assert.Len(t, address, 1, "should be 1 address")
assert.True(t, address[0].Family().IsIPv4())
})
t.Run("ipv6", func(t *testing.T) {
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv6.example.com", false, true)
assert.Len(t, address, 1, "should be 1 address")
assert.True(t, address[0].Family().IsIPv6())
})
t.Run("ipv46", func(t *testing.T) {
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv46.example.com", true, true)
assert.Len(t, address, 2, "should be 2 address")
assert.True(t, address[0].Family().IsIPv4())
assert.True(t, address[1].Family().IsIPv6())
})
})
}

View File

@@ -1,55 +1,66 @@
package dns package dns
import ( import (
"context" "github.com/xtls/xray-core/common"
"strconv" "github.com/xtls/xray-core/common/matcher/str"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher" "github.com/xtls/xray-core/features"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
// StaticHosts represents static domain-ip mapping in DNS server. // StaticHosts represents static domain-ip mapping in DNS server.
type StaticHosts struct { type StaticHosts struct {
ips [][]net.Address ips [][]net.Address
matchers *strmatcher.MatcherGroup matchers *str.MatcherGroup
} }
// NewStaticHosts creates a new StaticHosts instance. // NewStaticHosts creates a new StaticHosts instance.
func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) { func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
g := new(strmatcher.MatcherGroup) g := new(str.MatcherGroup)
sh := &StaticHosts{ sh := &StaticHosts{
ips: make([][]net.Address, len(hosts)+16), ips: make([][]net.Address, len(hosts)+len(legacy)+16),
matchers: g, matchers: g,
} }
if legacy != nil {
features.PrintDeprecatedFeatureWarning("simple host mapping")
for domain, ip := range legacy {
matcher, err := str.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 { for _, mapping := range hosts {
matcher, err := toStrMatcher(mapping.Type, mapping.Domain) matcher, err := toStrMatcher(mapping.Type, mapping.Domain)
if err != nil { 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) id := g.Add(matcher)
ips := make([]net.Address, 0, len(mapping.Ip)+1) ips := make([]net.Address, 0, len(mapping.Ip)+1)
switch { switch {
case len(mapping.ProxiedDomain) > 0: case len(mapping.ProxiedDomain) > 0:
if mapping.ProxiedDomain[0] == '#' { ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
rcode, err := strconv.Atoi(mapping.ProxiedDomain[1:])
if err != nil {
return nil, err
}
ips = append(ips, dns.RCodeError(rcode))
} else {
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
}
case len(mapping.Ip) > 0: case len(mapping.Ip) > 0:
for _, ip := range mapping.Ip { for _, ip := range mapping.Ip {
addr := net.IPAddress(ip) addr := net.IPAddress(ip)
if addr == nil { if addr == nil {
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) ips = append(ips, addr)
} }
default:
return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
} }
sh.ips[id] = ips sh.ips[id] = ips
@@ -58,7 +69,7 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
return sh, nil return sh, nil
} }
func filterIP(ips []net.Address, option dns.IPOption) []net.Address { func filterIP(ips []net.Address, option *dns.IPOption) []net.Address {
filtered := make([]net.Address, 0, len(ips)) filtered := make([]net.Address, 0, len(ips))
for _, ip := range ips { for _, ip := range ips {
if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) { if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) {
@@ -68,51 +79,36 @@ func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
return filtered return filtered
} }
func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) { func (h *StaticHosts) lookupInternal(domain string) []net.Address {
ips := make([]net.Address, 0) var ips []net.Address
found := false
for _, id := range h.matchers.Match(domain) { for _, id := range h.matchers.Match(domain) {
for _, v := range h.ips[id] {
if err, ok := v.(dns.RCodeError); ok {
if uint16(err) == 0 {
return nil, dns.ErrEmptyResponse
}
return nil, err
}
}
ips = append(ips, h.ips[id]...) ips = append(ips, h.ips[id]...)
found = true
} }
if !found { if len(ips) == 1 && ips[0].Family().IsDomain() {
return nil, nil return ips
} }
return ips, nil return ips
} }
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) { func (h *StaticHosts) lookup(domain string, option *dns.IPOption, maxDepth int) []net.Address {
switch addrs, err := h.lookupInternal(domain); { switch addrs := h.lookupInternal(domain); {
case err != nil:
return nil, err
case len(addrs) == 0: // Not recorded in static hosts, return nil case len(addrs) == 0: // Not recorded in static hosts, return nil
return addrs, nil return nil
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it") newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog()
if maxDepth > 0 { if maxDepth > 0 {
unwrapped, err := h.lookup(addrs[0].Domain(), option, maxDepth-1) unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1)
if err != nil {
return nil, err
}
if unwrapped != nil { if unwrapped != nil {
return unwrapped, nil return unwrapped
} }
} }
return addrs, nil return addrs
default: // IP record found, return a non-nil IP array default: // IP record found, return a non-nil IP array
return filterIP(addrs, option), nil return filterIP(addrs, option)
} }
} }
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts. // Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) ([]net.Address, error) { func (h *StaticHosts) Lookup(domain string, option *dns.IPOption) []net.Address {
return h.lookup(domain, option, 5) return h.lookup(domain, option, 5)
} }

View File

@@ -4,8 +4,10 @@ import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
. "github.com/xtls/xray-core/app/dns" . "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/matcher/domain"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
@@ -13,40 +15,35 @@ import (
func TestStaticHosts(t *testing.T) { func TestStaticHosts(t *testing.T) {
pb := []*Config_HostMapping{ pb := []*Config_HostMapping{
{ {
Type: DomainMatchingType_Subdomain, Type: domain.MatchingType_Full,
Domain: "lan",
ProxiedDomain: "#3",
},
{
Type: DomainMatchingType_Full,
Domain: "example.com", Domain: "example.com",
Ip: [][]byte{ Ip: [][]byte{
{1, 1, 1, 1}, {1, 1, 1, 1},
}, },
}, },
{ {
Type: DomainMatchingType_Full, Type: domain.MatchingType_Full,
Domain: "proxy.xray.com", Domain: "proxy.example.com",
Ip: [][]byte{ Ip: [][]byte{
{1, 2, 3, 4}, {1, 2, 3, 4},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
}, },
ProxiedDomain: "another-proxy.xray.com", ProxiedDomain: "another-proxy.example.com",
}, },
{ {
Type: DomainMatchingType_Full, Type: domain.MatchingType_Full,
Domain: "proxy2.xray.com", Domain: "proxy2.example.com",
ProxiedDomain: "proxy.xray.com", ProxiedDomain: "proxy.example.com",
}, },
{ {
Type: DomainMatchingType_Subdomain, Type: domain.MatchingType_Subdomain,
Domain: "example.cn", Domain: "example.cn",
Ip: [][]byte{ Ip: [][]byte{
{2, 2, 2, 2}, {2, 2, 2, 2},
}, },
}, },
{ {
Type: DomainMatchingType_Subdomain, Type: domain.MatchingType_Subdomain,
Domain: "baidu.com", Domain: "baidu.com",
Ip: [][]byte{ Ip: [][]byte{
{127, 0, 0, 1}, {127, 0, 0, 1},
@@ -55,18 +52,11 @@ func TestStaticHosts(t *testing.T) {
}, },
} }
hosts, err := NewStaticHosts(pb) hosts, err := NewStaticHosts(pb, nil)
common.Must(err) common.Must(err)
{ {
_, err := hosts.Lookup("example.com.lan", dns.IPOption{}) ips := hosts.Lookup("example.com", &dns.IPOption{
if dns.RCodeFromError(err) != 3 {
t.Error(err)
}
}
{
ips, _ := hosts.Lookup("example.com", dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })
@@ -79,33 +69,33 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
domain, _ := hosts.Lookup("proxy.xray.com", dns.IPOption{ domain := hosts.Lookup("proxy.example.com", &dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: false, IPv6Enable: false,
}) })
if len(domain) != 1 { if len(domain) != 1 {
t.Error("expect 1 domain, but got ", len(domain)) t.Error("expect 1 domain, but got ", len(domain))
} }
if diff := cmp.Diff(domain[0].Domain(), "another-proxy.xray.com"); diff != "" { if diff := cmp.Diff(domain[0].Domain(), "another-proxy.example.com"); diff != "" {
t.Error(diff) t.Error(diff)
} }
} }
{ {
domain, _ := hosts.Lookup("proxy2.xray.com", dns.IPOption{ domain := hosts.Lookup("proxy2.example.com", &dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: false, IPv6Enable: false,
}) })
if len(domain) != 1 { if len(domain) != 1 {
t.Error("expect 1 domain, but got ", len(domain)) t.Error("expect 1 domain, but got ", len(domain))
} }
if diff := cmp.Diff(domain[0].Domain(), "another-proxy.xray.com"); diff != "" { if diff := cmp.Diff(domain[0].Domain(), "another-proxy.example.com"); diff != "" {
t.Error(diff) t.Error(diff)
} }
} }
{ {
ips, _ := hosts.Lookup("www.example.cn", dns.IPOption{ ips := hosts.Lookup("www.example.cn", &dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })
@@ -118,7 +108,7 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
ips, _ := hosts.Lookup("baidu.com", dns.IPOption{ ips := hosts.Lookup("baidu.com", &dns.IPOption{
IPv4Enable: false, IPv4Enable: false,
IPv6Enable: true, IPv6Enable: true,
}) })

View File

@@ -6,12 +6,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/matcher/geoip"
"github.com/xtls/xray-core/common/matcher/str"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" core "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
) )
@@ -21,27 +20,34 @@ type Server interface {
// Name of the Client. // Name of the Client.
Name() string Name() string
// QueryIP sends IP queries to its configured server. // QueryIP sends IP queries to its configured server.
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, cs CacheStrategy) ([]net.IP, error)
} }
// Client is the interface for DNS client. // Client is the interface for DNS client.
type Client struct { type Client struct {
server Server server Server
clientIP net.IP
skipFallback bool skipFallback bool
domains []string expectIPs []*geoip.GeoIPMatcher
expectedIPs []*router.GeoIPMatcher domainMatcher str.MatcherGroup
unexpectedIPs []*router.GeoIPMatcher originRules []*NameServer_OriginalRule
actPrior bool
actUnprior bool
tag string
timeoutMs time.Duration
finalQuery bool
ipOption *dns.IPOption
checkSystem bool
} }
func (c Client) findRule(idx uint32) string {
for _, r := range c.originRules {
if idx <= r.Size {
return r.Rule
}
idx -= r.Size
}
return "unknown rule"
}
var errExpectedIPNonMatch = errors.New("expectIPs not match")
// NewServer creates a name server object according to the network destination url. // NewServer creates a name server object according to the network destination url.
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) (Server, error) { func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, error) {
if address := dest.Address; address.Family().IsDomain() { if address := dest.Address; address.Family().IsDomain() {
u, err := url.Parse(address.Domain()) u, err := url.Parse(address.Domain())
if err != nil { if err != nil {
@@ -50,61 +56,37 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
switch { switch {
case strings.EqualFold(u.String(), "localhost"): case strings.EqualFold(u.String(), "localhost"):
return NewLocalNameServer(), nil return NewLocalNameServer(), nil
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
return NewDoHNameServer(u, dispatcher, false, disableCache, clientIP), nil return NewDoHNameServer(u, dispatcher)
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
return NewDoHNameServer(u, dispatcher, true, disableCache, clientIP), nil return NewDoHLocalNameServer(u), 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, "quic+local"): // DNS-over-QUIC Local mode 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)
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
return NewTCPLocalNameServer(u, disableCache, clientIP)
case strings.EqualFold(u.String(), "fakedns"): case strings.EqualFold(u.String(), "fakedns"):
var fd dns.FakeDNSEngine return NewFakeDNSServer(), nil
err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
fd = fdns
})
if err != nil {
return nil, err
}
return NewFakeDNSServer(fd), nil
} }
} }
if dest.Network == net.Network_Unknown { if dest.Network == net.Network_Unknown {
dest.Network = net.Network_UDP dest.Network = net.Network_UDP
} }
if dest.Network == net.Network_UDP { // UDP classic DNS mode if dest.Network == net.Network_UDP { // UDP classic DNS mode
return NewClassicNameServer(dest, dispatcher, disableCache, 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. // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
func NewClient( func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container geoip.GeoIPMatcherContainer) (*Client, error) {
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) {
client := &Client{} client := &Client{}
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
// Create a new server for each client for now // Create a new server for each client for now
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, clientIP) server, err := NewServer(ns.Address.AsDestination(), dispatcher)
if err != nil { 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 { if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...) ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule) ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
@@ -112,191 +94,115 @@ func NewClient(
// Because the `localhost` DNS client will append len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule. // Because the `localhost` DNS client will append 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). // 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. // 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: // ;)
// https://github.com/v2fly/v2ray-core/issues/529 /*
// https://github.com/v2fly/v2ray-core/issues/719 for i := 0; i < len(localTLDsAndDotlessDomains); i++ {
for i := 0; i < len(localTLDsAndDotlessDomains); i++ { *matcherInfos = append(*matcherInfos, DomainMatcherInfo{
*matcherInfos = append(*matcherInfos, &DomainMatcherInfo{ clientIdx: uint16(0),
clientIdx: uint16(0), domainRuleIdx: uint16(0),
domainRuleIdx: uint16(0), })
}) }
} */
} }
// Establish domain rules // Establish domain rules
var rules []string var domainMatcher = str.MatcherGroup{}
ruleCurr := 0
ruleIter := 0
for _, domain := range ns.PrioritizedDomain { for _, domain := range ns.PrioritizedDomain {
domainRule, err := toStrMatcher(domain.Type, domain.Domain) domainRule, err := toStrMatcher(domain.Type, domain.Value)
if err != nil { 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) {
rule := ns.OriginalRules[ruleCurr]
if ruleCurr >= len(rules) {
rules = append(rules, rule.Rule)
}
ruleIter++
if ruleIter >= int(rule.Size) {
ruleIter = 0
ruleCurr++
}
} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
rules = append(rules, domainRule.String())
ruleCurr++
}
err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
if err != nil {
return errors.New("failed to create prioritized domain").Base(err).AtWarning()
} }
domainMatcher.Add(domainRule)
} }
// Establish expected IPs // Establish expected IPs
var expectedMatchers []*router.GeoIPMatcher var ipMatchers []*geoip.GeoIPMatcher
for _, geoip := range ns.ExpectedGeoip { for _, geoip := range ns.Geoip {
matcher, err := router.GlobalGeoIPContainer.Add(geoip) matcher, err := container.Add(geoip)
if err != nil { 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) ipMatchers = append(ipMatchers, 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)
} }
if len(clientIP) > 0 { if len(clientIP) > 0 {
switch ns.Address.Address.GetAddress().(type) { switch ns.Address.Address.GetAddress().(type) {
case *net.IPOrDomain_Domain: 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: 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.server = server
client.skipFallback = ns.SkipFallback client.clientIP = clientIP
client.domains = rules client.expectIPs = ipMatchers
client.expectedIPs = expectedMatchers client.originRules = ns.OriginalRules
client.unexpectedIPs = unexpectedMatchers client.domainMatcher = domainMatcher
client.actPrior = ns.ActPrior
client.actUnprior = ns.ActUnprior
client.tag = tag
client.timeoutMs = timeoutMs
client.finalQuery = ns.FinalQuery
client.ipOption = &ipOption
client.checkSystem = checkSystem
return nil return nil
}) })
return client, err 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. // Name returns the server name the client manages.
func (c *Client) Name() string { func (c *Client) Name() string {
return c.server.Name() return c.server.Name()
} }
func (c *Client) IsFinalQuery() bool { // QueryIP send DNS query to the name server with the client's IP.
return c.finalQuery func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, cs CacheStrategy) ([]net.IP, error) {
} ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, cs)
// 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)
cancel() cancel()
if err != nil { if err != nil {
return nil, 0, err return ips, err
} }
return c.MatchExpectedIPs(domain, ips)
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
} }
func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption) dns.IPOption { // MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones.
switch queryStrategy { func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) {
case QueryStrategy_USE_IP: if len(c.expectIPs) == 0 {
return ipOption return ips, nil
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
} }
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
} }

View File

@@ -3,136 +3,252 @@ package dns
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/tls"
go_errors "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "sync"
"sync/atomic"
"time" "time"
utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common" "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/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
"github.com/xtls/xray-core/common/session" "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" dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
"golang.org/x/net/http2" "golang.org/x/net/dns/dnsmessage"
) )
// DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format, // DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format,
// which is compatible with traditional dns over udp(RFC1035), // which is compatible with traditional dns over udp(RFC1035),
// thus most of the DOH implementation is copied from udpns.go // thus most of the DOH implementation is copied from udpns.go
type DoHNameServer struct { type DoHNameServer struct {
cacheController *CacheController dispatcher routing.Dispatcher
httpClient *http.Client sync.RWMutex
dohURL string ips map[string]record
clientIP net.IP pub *pubsub.Service
cleanup *task.Periodic
reqID uint32
clientIP net.IP
httpClient *http.Client
dohURL string
name string
} }
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving. // NewDoHNameServer creates DOH server object for remote resolving
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, clientIP net.IP) *DoHNameServer { func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServer, error) {
url.Scheme = "https" newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
mode := "DOH" s := baseDOHNameServer(url, "DOH")
if dispatcher == nil {
mode = "DOHL" s.dispatcher = dispatcher
} tr := &http.Transport{
errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c) MaxIdleConns: 30,
s := &DoHNameServer{ IdleConnTimeout: 90 * time.Second,
cacheController: NewCacheController(mode+"//"+url.Host, disableCache), TLSHandshakeTimeout: 30 * time.Second,
dohURL: url.String(), ForceAttemptHTTP2: true,
clientIP: clientIP, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
} dispatcherCtx := context.Background()
s.httpClient = &http.Client{
Transport: &http2.Transport{ dest, err := net.ParseDestination(network + ":" + addr)
IdleConnTimeout: net.ConnIdleTimeout, if err != nil {
ReadIdleTimeout: net.ChromeH2KeepAlivePeriod, return nil, err
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { }
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil { dispatcherCtx = session.ContextWithContent(dispatcherCtx, session.ContentFromContext(ctx))
return nil, err dispatcherCtx = session.ContextWithInbound(dispatcherCtx, session.InboundFromContext(ctx))
} dispatcherCtx = log.ContextWithAccessMessage(dispatcherCtx, &log.AccessMessage{
var conn net.Conn From: "DoH",
if dispatcher != nil { To: s.dohURL,
dnsCtx := toDnsContext(ctx, s.dohURL) Status: log.AccessAccepted,
if h2c { Reason: "",
dnsCtx = session.ContextWithMitmAlpn11(dnsCtx, false) // for insurance })
dnsCtx = session.ContextWithMitmServerName(dnsCtx, url.Hostname())
} link, err := s.dispatcher.Dispatch(dispatcherCtx, dest)
link, err := dispatcher.Dispatch(dnsCtx, dest) select {
select { case <-ctx.Done():
case <-ctx.Done(): return nil, ctx.Err()
return nil, ctx.Err() default:
default:
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
cc := common.ChainedClosable{} log.Record(&log.AccessMessage{
if cw, ok := link.Writer.(common.Closable); ok { From: "DoH",
cc = append(cc, cw) To: s.dohURL,
} Status: log.AccessAccepted,
if cr, ok := link.Reader.(common.Closable); ok { Detour: "local",
cc = append(cc, cr) })
}
conn = cnc.NewConnection( cc := common.ChainedClosable{}
cnc.ConnectionInputMulti(link.Writer), if cw, ok := link.Writer.(common.Closable); ok {
cnc.ConnectionOutputMulti(link.Reader), cc = append(cc, cw)
cnc.ConnectionOnClose(cc), }
) if cr, ok := link.Reader.(common.Closable); ok {
} else { cc = append(cc, cr)
log.Record(&log.AccessMessage{ }
From: "DNS", return cnc.NewConnection(
To: s.dohURL, cnc.ConnectionInputMulti(link.Writer),
Status: log.AccessAccepted, cnc.ConnectionOutputMulti(link.Reader),
Detour: "local", cnc.ConnectionOnClose(cc),
}) ), nil
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
},
}, },
} }
s.httpClient = &http.Client{
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: "DoH",
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 return s
} }
// Name implements Server. // Name implements Server.
func (s *DoHNameServer) Name() string { 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 := s.ips[req.domain]
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 *DoHNameServer) newReqID() uint16 { 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) { func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", domain) newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
if s.Name()+"." == "DOH//"+domain { if s.name+"." == "DOH//"+domain {
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead.") newError(s.name, " tries to resolve itself! Use IP or set \"hosts\" instead.").AtError().WriteToLog(session.ExportIDToError(ctx))
noResponseErrCh <- errors.New("tries to resolve itself!", s.Name())
return return
} }
// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467 reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
// 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))))
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -145,7 +261,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
go func(r *dnsRequest) { go func(r *dnsRequest) {
// generate new context for each req, using same context // generate new context for each req, using same context
// may cause reqs all aborted if any one encounter an error // may cause reqs all aborted if any one encounter an error
dnsCtx := ctx dnsCtx := context.Background()
// reserve internal dns server requested Inbound // reserve internal dns server requested Inbound
if inbound := session.InboundFromContext(ctx); inbound != nil { if inbound := session.InboundFromContext(ctx); inbound != nil {
@@ -158,7 +274,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
}) })
// forced to use mux for DOH // forced to use mux for DOH
// dnsCtx = session.ContextWithMuxPreferred(dnsCtx, true) // dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true)
var cancel context.CancelFunc var cancel context.CancelFunc
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline) dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
@@ -166,23 +282,20 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", domain) newError("failed to pack dns query for ", domain).Base(err).AtError().WriteToLog()
noResponseErrCh <- err
return return
} }
resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes()) resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to retrieve response for ", domain) newError("failed to retrieve response for ", domain).Base(err).AtError().WriteToLog()
noResponseErrCh <- err
return return
} }
rec, err := parseResponse(resp) rec, err := parseResponse(resp)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", domain) newError("failed to handle DOH response for ", domain).Base(err).AtError().WriteToLog()
noResponseErrCh <- err
return return
} }
s.cacheController.updateIP(r, rec) s.updateIP(r, rec)
}(req) }(req)
} }
} }
@@ -197,8 +310,6 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
req.Header.Add("Accept", "application/dns-message") req.Header.Add("Accept", "application/dns-message")
req.Header.Add("Content-Type", "application/dns-message") req.Header.Add("Content-Type", "application/dns-message")
req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000))))
hc := s.httpClient hc := s.httpClient
resp, err := hc.Do(req.WithContext(ctx)) resp, err := hc.Do(req.WithContext(ctx))
@@ -208,57 +319,112 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
io.Copy(io.Discard, resp.Body) // flush resp.Body so that the conn is reusable io.Copy(ioutil.Discard, resp.Body) // flush resp.Body so that the conn is reusable
return nil, fmt.Errorf("DOH server returned code %d", resp.StatusCode) return nil, fmt.Errorf("DOH server returned code %d", resp.StatusCode)
} }
return io.ReadAll(resp.Body) return ioutil.ReadAll(resp.Body)
}
func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock()
record, found := s.ips[domain]
s.RUnlock()
if !found {
return nil, errRecordNotFound
}
var ips []net.Address
var lastErr error
if option.IPv6Enable && record.AAAA != nil && record.AAAA.RCode == dnsmessage.RCodeSuccess {
aaaa, err := record.AAAA.getIPs()
if err != nil {
lastErr = err
}
ips = append(ips, aaaa...)
}
if option.IPv4Enable && record.A != nil && record.A.RCode == dnsmessage.RCodeSuccess {
a, err := record.A.getIPs()
if err != nil {
lastErr = err
}
ips = append(ips, a...)
}
if len(ips) > 0 {
return toNetIP(ips)
}
if lastErr != nil {
return nil, lastErr
}
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
return nil, dns_feature.ErrEmptyResponse
}
return nil, errRecordNotFound
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { // nolint: dupl func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) { // nolint: dupl
fqdn := Fqdn(domain) fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache { if cs == CacheStrategy_Cache_DISABLE {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name()) newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
} else { } else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option) ips, err := s.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) { if err != errRecordNotFound {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips) if cs == CacheStrategy_Cache_NOERROR && err == nil {
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
return ips, ttl, err log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
return ips, err
}
} }
} }
noResponseErrCh := make(chan error, 2) // ipv4 and ipv6 belong to different subscription groups
s.sendQuery(ctx, noResponseErrCh, fqdn, option) 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() start := time.Now()
if sub4 != nil { for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
return ips, err
}
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, 0, ctx.Err() return nil, ctx.Err()
case err := <-noResponseErrCh: case <-done:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
} }
} }
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
} }

View File

@@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
. "github.com/xtls/xray-core/app/dns" . "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
@@ -17,12 +18,12 @@ func TestDOHNameServer(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil)) s := NewDoHLocalNameServer(url)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) }, CacheStrategy_Cache_ALL)
cancel() cancel()
common.Must(err) common.Must(err)
if len(ips) == 0 { if len(ips) == 0 {
@@ -34,72 +35,26 @@ func TestDOHNameServerWithCache(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil)) s := NewDoHLocalNameServer(url)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) }, CacheStrategy_Cache_ALL)
cancel() cancel()
common.Must(err) common.Must(err)
if len(ips) == 0 { if len(ips) == 0 {
t.Error("expect some ips, but got 0") t.Error("expect some ips, but got 0")
} }
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx2, cancel := context.WithTimeout(context.Background(), time.Second*2)
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, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) }, CacheStrategy_Cache_ALL)
cancel() cancel()
common.Must(err) common.Must(err)
if r := cmp.Diff(ips2, ips); r != "" { if r := cmp.Diff(ips2, ips); r != "" {
t.Fatal(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")
}
}
}

View File

@@ -3,8 +3,8 @@ package dns
import ( import (
"context" "context"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
@@ -12,35 +12,32 @@ type FakeDNSServer struct {
fakeDNSEngine dns.FakeDNSEngine fakeDNSEngine dns.FakeDNSEngine
} }
func NewFakeDNSServer(fd dns.FakeDNSEngine) *FakeDNSServer { func NewFakeDNSServer() *FakeDNSServer {
return &FakeDNSServer{fakeDNSEngine: fd} return &FakeDNSServer{}
} }
const FakeDNSName = "FakeDNS"
func (FakeDNSServer) Name() string { func (FakeDNSServer) Name() string {
return "FakeDNS" return FakeDNSName
} }
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, _ dns.IPOption, _ CacheStrategy) ([]net.IP, error) {
if f.fakeDNSEngine == nil { 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 {
var ips []net.Address return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError()
if fkr0, ok := f.fakeDNSEngine.(dns.FakeDNSEngineRev0); ok { }
ips = fkr0.GetFakeIPForDomain3(domain, opt.IPv4Enable, opt.IPv6Enable)
} else {
ips = f.fakeDNSEngine.GetFakeIPForDomain(domain)
} }
ips := f.fakeDNSEngine.GetFakeIPForDomain(domain)
netIP, err := toNetIP(ips) netIP, err := toNetIP(ips)
if err != nil { 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, nil
return netIP, 1, nil // fakeIP ttl is 1
}
return nil, 0, dns.ErrEmptyResponse
} }

View File

@@ -2,10 +2,7 @@ package dns
import ( import (
"context" "context"
"time"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/dns/localdns" "github.com/xtls/xray-core/features/dns/localdns"
@@ -17,17 +14,24 @@ type LocalNameServer struct {
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) { func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ CacheStrategy) ([]net.IP, error) {
var ips []net.IP
var err error
start := time.Now() switch {
ips, ttl, err = s.client.LookupIP(domain, option) case option.IPv4Enable && option.IPv6Enable:
ips, err = s.client.LookupIP(domain)
if len(ips) > 0 { case option.IPv4Enable:
errors.LogInfo(ctx, "Localhost got answer: ", domain, " -> ", ips) ips, err = s.client.LookupIPv4(domain)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) case option.IPv6Enable:
ips, err = s.client.LookupIPv6(domain)
} }
return if len(ips) > 0 {
newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
}
return ips, err
} }
// Name implements Server. // Name implements Server.
@@ -37,13 +41,13 @@ func (s *LocalNameServer) Name() string {
// NewLocalNameServer creates localdns server object for directly lookup in system DNS. // NewLocalNameServer creates localdns server object for directly lookup in system DNS.
func NewLocalNameServer() *LocalNameServer { func NewLocalNameServer() *LocalNameServer {
errors.LogInfo(context.Background(), "DNS: created localhost client") newError("DNS: created localhost client").AtInfo().WriteToLog()
return &LocalNameServer{ return &LocalNameServer{
client: localdns.New(), client: localdns.New(),
} }
} }
// NewLocalDNSClient creates localdns client object for directly lookup in system DNS. // NewLocalDNSClient creates localdns client object for directly lookup in system DNS.
func NewLocalDNSClient(ipOption dns.IPOption) *Client { func NewLocalDNSClient() *Client {
return &Client{server: NewLocalNameServer(), ipOption: &ipOption} return &Client{server: NewLocalNameServer()}
} }

View File

@@ -7,17 +7,17 @@ import (
. "github.com/xtls/xray-core/app/dns" . "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
func TestLocalNameServer(t *testing.T) { func TestLocalNameServer(t *testing.T) {
s := NewLocalNameServer() s := NewLocalNameServer()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
FakeEnable: false, }, CacheStrategy_Cache_ALL)
})
cancel() cancel()
common.Must(err) common.Must(err)
if len(ips) == 0 { if len(ips) == 0 {

View File

@@ -1,59 +1,67 @@
package dns package dns
import ( import (
"bytes"
"context" "context"
"encoding/binary"
go_errors "errors"
"net/url" "net/url"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/quic-go/quic-go" "github.com/lucas-clemente/quic-go"
"golang.org/x/net/dns/dnsmessage"
"golang.org/x/net/http2"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
"github.com/xtls/xray-core/common/session" "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" dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/tls"
"golang.org/x/net/http2"
) )
// NextProtoDQ - During connection establishment, DNS/QUIC support is indicated // NextProtoDQ - During connection establishment, DNS/QUIC support is indicated
// by selecting the ALPN token "dq" in the crypto handshake. // 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 // QUICNameServer implemented DNS over QUIC
type QUICNameServer struct { type QUICNameServer struct {
sync.RWMutex sync.RWMutex
cacheController *CacheController ips map[string]record
destination *net.Destination pub *pubsub.Service
connection *quic.Conn cleanup *task.Periodic
clientIP net.IP reqID uint32
name string
destination net.Destination
session quic.Session
} }
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving // NewQUICNameServer creates DNS-over-QUIC client object for local resolving
func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICNameServer, error) { func NewQUICNameServer(url *url.URL) (*QUICNameServer, error) {
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String()) newError("DNS: created Local DNS-over-QUIC client for ", url.String()).AtInfo().WriteToLog()
var err error var err error
port := net.Port(853) port := net.Port(784)
if url.Port() != "" { if url.Port() != "" {
port, err = net.PortFromString(url.Port()) port, err = net.PortFromString(url.Port())
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port) dest := net.UDPDestination(net.DomainAddress(url.Hostname()), port)
s := &QUICNameServer{ s := &QUICNameServer{
cacheController: NewCacheController(url.String(), disableCache), ips: make(map[string]record),
destination: &dest, pub: pubsub.NewService(),
clientIP: clientIP, name: url.String(),
destination: dest,
}
s.cleanup = &task.Periodic{
Interval: time.Minute,
Execute: s.Cleanup,
} }
return s, nil return s, nil
@@ -61,17 +69,91 @@ func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICN
// Name returns client name // Name returns client name
func (s *QUICNameServer) Name() string { 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 := s.ips[req.domain]
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 { 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) { func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", domain) 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 var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -84,17 +166,22 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
go func(r *dnsRequest) { go func(r *dnsRequest) {
// generate new context for each req, using same context // generate new context for each req, using same context
// may cause reqs all aborted if any one encounter an error // may cause reqs all aborted if any one encounter an error
dnsCtx := ctx dnsCtx := context.Background()
// reserve internal dns server requested Inbound // reserve internal dns server requested Inbound
if inbound := session.InboundFromContext(ctx); inbound != nil { if inbound := session.InboundFromContext(ctx); inbound != nil {
dnsCtx = session.ContextWithInbound(dnsCtx, inbound) dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
} }
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{ dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
Protocol: "quic", Protocol: "quic",
SkipDNSResolve: true, SkipDNSResolve: true,
}) })
dnsCtx = log.ContextWithAccessMessage(dnsCtx, &log.AccessMessage{
From: "DoQ",
To: s.name,
Status: log.AccessAccepted,
Reason: "",
})
var cancel context.CancelFunc var cancel context.CancelFunc
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline) dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
@@ -102,37 +189,19 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query") newError("failed to pack dns query").Base(err).AtError().WriteToLog()
noResponseErrCh <- err
return 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) conn, err := s.openStream(dnsCtx)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to open quic connection") newError("failed to open quic session").Base(err).AtError().WriteToLog()
noResponseErrCh <- err
return return
} }
_, err = conn.Write(dnsReqBuf.Bytes()) _, err = conn.Write(b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query") newError("failed to send query").Base(err).AtError().WriteToLog()
noResponseErrCh <- err
return return
} }
@@ -140,87 +209,126 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
respBuf := buf.New() respBuf := buf.New()
defer respBuf.Release() defer respBuf.Release()
n, err := respBuf.ReadFullFrom(conn, 2) n, err := respBuf.ReadFrom(conn)
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") newError("failed to read response").Base(err).AtError().WriteToLog()
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
return return
} }
rec, err := parseResponse(respBuf.Bytes()) rec, err := parseResponse(respBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle response") newError("failed to handle response").Base(err).AtError().WriteToLog()
noResponseErrCh <- err
return return
} }
s.cacheController.updateIP(r, rec) s.updateIP(r, rec)
}(req) }(req)
} }
} }
// QueryIP is called from dns.Server->queryIPTimeout func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { s.RLock()
fqdn := Fqdn(domain) record, found := s.ips[domain]
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option) s.RUnlock()
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache { if !found {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name()) return nil, errRecordNotFound
} else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
} }
noResponseErrCh := make(chan error, 2) var ips []net.Address
s.sendQuery(ctx, noResponseErrCh, fqdn, option) var lastErr error
start := time.Now() if option.IPv6Enable && record.AAAA != nil && record.AAAA.RCode == dnsmessage.RCodeSuccess {
aaaa, err := record.AAAA.getIPs()
if sub4 != nil { if err != nil {
select { lastErr = err
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
} }
ips = append(ips, aaaa...)
} }
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option) if option.IPv4Enable && record.A != nil && record.A.RCode == dnsmessage.RCodeSuccess {
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) a, err := record.A.getIPs()
return ips, ttl, err if err != nil {
lastErr = err
}
ips = append(ips, a...)
}
if len(ips) > 0 {
return toNetIP(ips)
}
if lastErr != nil {
return nil, lastErr
}
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
return nil, dns_feature.ErrEmptyResponse
}
return nil, errRecordNotFound
} }
func isActive(s *quic.Conn) bool { // QueryIP is called from dns.Server->queryIPTimeout
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) {
fqdn := Fqdn(domain)
if cs == CacheStrategy_Cache_DISABLE {
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
} else {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
if cs == CacheStrategy_Cache_NOERROR && err == nil {
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
return ips, err
}
}
}
// 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()
for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
return ips, err
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
}
}
}
func isActive(s quic.Session) bool {
select { select {
case <-s.Context().Done(): case <-s.Context().Done():
return false return false
@@ -229,17 +337,17 @@ func isActive(s *quic.Conn) bool {
} }
} }
func (s *QUICNameServer) getConnection() (*quic.Conn, error) { func (s *QUICNameServer) getSession() (quic.Session, error) {
var conn *quic.Conn var session quic.Session
s.RLock() s.RLock()
conn = s.connection session = s.session
if conn != nil && isActive(conn) { if session != nil && isActive(session) {
s.RUnlock() s.RUnlock()
return conn, nil return session, nil
} }
if conn != nil { if session != nil {
// we're recreating the connection, let's create a new one // we're recreating the session, let's create a new one
_ = conn.CloseWithError(0, "") _ = session.CloseWithError(0, "")
} }
s.RUnlock() s.RUnlock()
@@ -247,48 +355,40 @@ func (s *QUICNameServer) getConnection() (*quic.Conn, error) {
defer s.Unlock() defer s.Unlock()
var err error var err error
conn, err = s.openConnection() session, err = s.openSession()
if err != nil { if err != nil {
// This does not look too nice, but QUIC (or maybe quic-go) // This does not look too nice, but QUIC (or maybe quic-go)
// doesn't seem stable enough. // doesn't seem stable enough.
// Maybe retransmissions aren't fully implemented in quic-go? // Maybe retransmissions aren't fully implemented in quic-go?
// Anyways, the simple solution is to make a second try when // Anyways, the simple solution is to make a second try when
// it fails to open the QUIC connection. // it fails to open the QUIC session.
conn, err = s.openConnection() session, err = s.openSession()
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
s.connection = conn s.session = session
return conn, nil return session, nil
} }
func (s *QUICNameServer) openConnection() (*quic.Conn, error) { func (s *QUICNameServer) openSession() (quic.Session, error) {
tlsConfig := tls.Config{} tlsConfig := tls.Config{}
quicConfig := &quic.Config{ quicConfig := &quic.Config{}
HandshakeIdleTimeout: handshakeTimeout,
} session, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
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)
log.Record(&log.AccessMessage{
From: "DNS",
To: s.destination,
Status: log.AccessAccepted,
Detour: "local",
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return conn, nil return session, nil
} }
func (s *QUICNameServer) openStream(ctx context.Context) (*quic.Stream, error) { func (s *QUICNameServer) openStream(ctx context.Context) (quic.Stream, error) {
conn, err := s.getConnection() session, err := s.getSession()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// open a new stream // open a new stream
return conn.OpenStreamSync(ctx) return session.OpenStreamSync(ctx)
} }

View File

@@ -7,81 +7,54 @@ import (
"time" "time"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
. "github.com/xtls/xray-core/app/dns" . "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
) )
func TestQUICNameServer(t *testing.T) { func TestQUICNameServer(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com") url, err := url.Parse("quic://dns.adguard.com")
common.Must(err) common.Must(err)
s, err := NewQUICNameServer(url, false, net.IP(nil)) s, err := NewQUICNameServer(url)
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) }, CacheStrategy_Cache_ALL)
cancel() cancel()
common.Must(err) common.Must(err)
if len(ips) == 0 { if len(ips) == 0 {
t.Error("expect some ips, but got 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{
func TestQUICNameServerWithCache(t *testing.T) {
url, err := url.Parse("quic://dns.adguard.com")
common.Must(err)
s, err := NewQUICNameServer(url)
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) }, CacheStrategy_Cache_ALL)
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", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, CacheStrategy_Cache_ALL)
cancel() cancel()
common.Must(err) common.Must(err)
if r := cmp.Diff(ips2, ips); r != "" { if r := cmp.Diff(ips2, ips); r != "" {
t.Fatal(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")
}
}
}

View File

@@ -1,249 +0,0 @@
package dns
import (
"bytes"
"context"
"encoding/binary"
go_errors "errors"
"net/url"
"sync/atomic"
"time"
"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"
dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet"
)
// 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
}
// 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)
if err != nil {
return nil, err
}
s.dial = func(ctx context.Context) (net.Conn, error) {
link, err := dispatcher.Dispatch(toDnsContext(ctx, s.destination.String()), *s.destination)
if err != nil {
return nil, err
}
return cnc.NewConnection(
cnc.ConnectionInputMulti(link.Writer),
cnc.ConnectionOutputMulti(link.Reader),
), nil
}
return s, nil
}
// 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)
if err != nil {
return nil, err
}
s.dial = func(ctx context.Context) (net.Conn, error) {
return internet.DialSystem(ctx, *s.destination, nil)
}
return s, nil
}
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
port := net.Port(53)
if url.Port() != "" {
var err error
if port, err = net.PortFromString(url.Port()); 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,
}
return s, nil
}
// Name implements Server.
func (s *TCPNameServer) Name() string {
return s.cacheController.name
}
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)
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
var deadline time.Time
if d, ok := ctx.Deadline(); ok {
deadline = d
} else {
deadline = time.Now().Add(time.Second * 5)
}
for _, req := range reqs {
go func(r *dnsRequest) {
dnsCtx := ctx
if inbound := session.InboundFromContext(ctx); inbound != nil {
dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
}
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
Protocol: "dns",
SkipDNSResolve: true,
})
var cancel context.CancelFunc
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
defer cancel()
b, err := dns.PackMessage(r.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
noResponseErrCh <- err
return
}
conn, err := s.dial(dnsCtx)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to dial namesever")
noResponseErrCh <- err
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
}
b.Release()
_, err = conn.Write(dnsReqBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query")
noResponseErrCh <- err
return
}
dnsReqBuf.Release()
respBuf := buf.New()
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
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
return
}
rec, err := parseResponse(respBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response")
noResponseErrCh <- err
return
}
s.cacheController.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)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
}

View File

@@ -1,107 +0,0 @@
package dns_test
import (
"context"
"net/url"
"testing"
"time"
"github.com/google/go-cmp/cmp"
. "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
dns_feature "github.com/xtls/xray-core/features/dns"
)
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))
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: true,
})
cancel()
common.Must(err)
if len(ips) == 0 {
t.Error("expect some ips, but got 0")
}
}
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))
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: true,
})
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_feature.IPOption{
IPv4Enable: true,
IPv6Enable: 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")
}
}
}

View File

@@ -2,18 +2,18 @@ package dns
import ( import (
"context" "context"
go_errors "errors"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
udp_proto "github.com/xtls/xray-core/common/protocol/udp" udp_proto "github.com/xtls/xray-core/common/protocol/udp"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
@@ -21,58 +21,73 @@ import (
"golang.org/x/net/dns/dnsmessage" "golang.org/x/net/dns/dnsmessage"
) )
// ClassicNameServer implemented traditional UDP DNS.
type ClassicNameServer struct { type ClassicNameServer struct {
sync.RWMutex sync.RWMutex
cacheController *CacheController name string
address *net.Destination address net.Destination
requests map[uint16]*udpDnsRequest ips map[string]record
udpServer *udp.Dispatcher requests map[uint16]dnsRequest
requestsCleanup *task.Periodic pub *pubsub.Service
reqID uint32 udpServer *udp.Dispatcher
clientIP net.IP cleanup *task.Periodic
} reqID uint32
type udpDnsRequest struct {
dnsRequest
ctx context.Context
} }
// NewClassicNameServer creates udp server object for remote resolving. // NewClassicNameServer creates udp server object for remote resolving.
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) *ClassicNameServer { func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher) *ClassicNameServer {
// default to 53 if unspecific // default to 53 if unspecific
if address.Port == 0 { if address.Port == 0 {
address.Port = net.Port(53) address.Port = net.Port(53)
} }
s := &ClassicNameServer{ s := &ClassicNameServer{
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache), address: address,
address: &address, ips: make(map[string]record),
requests: make(map[uint16]*udpDnsRequest), requests: make(map[uint16]dnsRequest),
clientIP: clientIP, pub: pubsub.NewService(),
name: strings.ToUpper(address.String()),
} }
s.requestsCleanup = &task.Periodic{ s.cleanup = &task.Periodic{
Interval: time.Minute, Interval: time.Minute,
Execute: s.RequestsCleanup, Execute: s.Cleanup,
} }
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse) s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr()) newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog()
return s return s
} }
// Name implements Server. // Name implements Server.
func (s *ClassicNameServer) Name() string { func (s *ClassicNameServer) Name() string {
return s.cacheController.name return s.name
} }
// RequestsCleanup clears expired items from cache // Cleanup clears expired items from cache
func (s *ClassicNameServer) RequestsCleanup() error { func (s *ClassicNameServer) Cleanup() error {
now := time.Now() now := time.Now()
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
if len(s.requests) == 0 { if len(s.ips) == 0 && len(s.requests) == 0 {
return errors.New(s.Name(), " nothing to do. stopping...") 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 {
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 { for id, req := range s.requests {
@@ -82,7 +97,7 @@ func (s *ClassicNameServer) RequestsCleanup() error {
} }
if len(s.requests) == 0 { if len(s.requests) == 0 {
s.requests = make(map[uint16]*udpDnsRequest) s.requests = make(map[uint16]dnsRequest)
} }
return nil return nil
@@ -92,7 +107,7 @@ func (s *ClassicNameServer) RequestsCleanup() error {
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) { func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
ipRec, err := parseResponse(packet.Payload.Bytes()) ipRec, err := parseResponse(packet.Payload.Bytes())
if err != nil { 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 return
} }
@@ -105,107 +120,187 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
} }
s.Unlock() s.Unlock()
if !ok { if !ok {
errors.LogError(ctx, s.Name(), " cannot find the pending request") newError(s.name, " cannot find the pending request").AtError().WriteToLog()
return return
} }
// if truncated, retry with EDNS0 option(udp payload size: 1350) var rec record
if ipRec.RawHeader.Truncated { switch req.reqType {
// if already has EDNS0 option, no need to retry case dnsmessage.TypeA:
if len(req.msg.Additionals) == 0 { rec.A = ipRec
// copy necessary meta data from original request case dnsmessage.TypeAAAA:
// and add EDNS0 option rec.AAAA = ipRec
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
}
} }
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()
newError(s.name, " updating IP records for domain:", domain).AtDebug().WriteToLog()
rec := s.ips[domain]
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 {
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 { func (s *ClassicNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1)) return uint16(atomic.AddUint32(&s.reqID, 1))
} }
func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) { func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) {
s.Lock() s.Lock()
defer s.Unlock()
id := req.msg.ID id := req.msg.ID
req.expire = time.Now().Add(time.Second * 8) req.expire = time.Now().Add(time.Second * 8)
s.requests[id] = req 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) { func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain) 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 { for _, req := range reqs {
udpReq := &udpDnsRequest{ s.addPendingRequest(req)
dnsRequest: *req,
ctx: ctx,
}
s.addPendingRequest(udpReq)
b, _ := dns.PackMessage(req.msg) b, _ := dns.PackMessage(req.msg)
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b) udpCtx := context.Background()
if inbound := session.InboundFromContext(ctx); inbound != nil {
udpCtx = session.ContextWithInbound(udpCtx, inbound)
}
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
Protocol: "dns",
})
udpCtx = log.ContextWithAccessMessage(udpCtx, &log.AccessMessage{
From: "DNS",
To: s.address,
Status: log.AccessAccepted,
Reason: "",
})
s.udpServer.Dispatch(udpCtx, s.address, b)
} }
} }
func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock()
record, found := s.ips[domain]
s.RUnlock()
if !found {
return nil, errRecordNotFound
}
var ips []net.Address
var lastErr error
if option.IPv4Enable {
a, err := record.A.getIPs()
if err != nil {
lastErr = err
}
ips = append(ips, a...)
}
if option.IPv6Enable {
aaaa, err := record.AAAA.getIPs()
if err != nil {
lastErr = err
}
ips = append(ips, aaaa...)
}
if len(ips) > 0 {
return toNetIP(ips)
}
if lastErr != nil {
return nil, lastErr
}
return nil, dns_feature.ErrEmptyResponse
}
// QueryIP implements Server. // QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) {
fqdn := Fqdn(domain) fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache { if cs == CacheStrategy_Cache_DISABLE {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name()) newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
} else { } else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option) ips, err := s.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) { if err != errRecordNotFound {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips) if cs == CacheStrategy_Cache_NOERROR && err == nil {
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
return ips, ttl, err log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
return ips, err
}
} }
} }
noResponseErrCh := make(chan error, 2) // ipv4 and ipv6 belong to different subscription groups
s.sendQuery(ctx, noResponseErrCh, fqdn, option) 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() start := time.Now()
if sub4 != nil { for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
return ips, err
}
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, 0, ctx.Err() return nil, ctx.Err()
case err := <-noResponseErrCh: case <-done:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
} }
} }
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
} }

16
app/dns/options.go Normal file
View File

@@ -0,0 +1,16 @@
package dns
import "github.com/xtls/xray-core/features/dns"
func isIPQuery(o *dns.IPOption) bool {
return o.IPv4Enable || o.IPv6Enable
}
func canQueryOnClient(o *dns.IPOption, c *Client) bool {
isIPClient := !(c.Name() == FakeDNSName)
return isIPClient && isIPQuery(o)
}
func isQuery(o *dns.IPOption) bool {
return !(o.IPv4Enable || o.IPv6Enable || o.FakeEnable)
}

View File

@@ -1,13 +1,15 @@
package command package command
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
import ( import (
"context" "context"
grpc "google.golang.org/grpc"
"github.com/xtls/xray-core/app/log" "github.com/xtls/xray-core/app/log"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
grpc "google.golang.org/grpc"
) )
type LoggerServer struct { type LoggerServer struct {
@@ -18,13 +20,13 @@ type LoggerServer struct {
func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) { func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) {
logger := s.V.GetFeature((*log.Instance)(nil)) logger := s.V.GetFeature((*log.Instance)(nil))
if logger == 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 { 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 { 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 return &RestartLoggerResponse{}, nil
} }

View File

@@ -1,12 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.25.0
// protoc v5.28.2 // protoc v3.14.0
// source: app/log/command/config.proto // source: app/log/command/config.proto
package command package command
import ( import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
@@ -20,6 +21,10 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct { type Config struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -28,9 +33,11 @@ type Config struct {
func (x *Config) Reset() { func (x *Config) Reset() {
*x = Config{} *x = Config{}
mi := &file_app_log_command_config_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_log_command_config_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Config) String() string { func (x *Config) String() string {
@@ -41,7 +48,7 @@ func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message { func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_log_command_config_proto_msgTypes[0] mi := &file_app_log_command_config_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -64,9 +71,11 @@ type RestartLoggerRequest struct {
func (x *RestartLoggerRequest) Reset() { func (x *RestartLoggerRequest) Reset() {
*x = RestartLoggerRequest{} *x = RestartLoggerRequest{}
mi := &file_app_log_command_config_proto_msgTypes[1] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_log_command_config_proto_msgTypes[1]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *RestartLoggerRequest) String() string { func (x *RestartLoggerRequest) String() string {
@@ -77,7 +86,7 @@ func (*RestartLoggerRequest) ProtoMessage() {}
func (x *RestartLoggerRequest) ProtoReflect() protoreflect.Message { func (x *RestartLoggerRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_log_command_config_proto_msgTypes[1] mi := &file_app_log_command_config_proto_msgTypes[1]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -100,9 +109,11 @@ type RestartLoggerResponse struct {
func (x *RestartLoggerResponse) Reset() { func (x *RestartLoggerResponse) Reset() {
*x = RestartLoggerResponse{} *x = RestartLoggerResponse{}
mi := &file_app_log_command_config_proto_msgTypes[2] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_log_command_config_proto_msgTypes[2]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *RestartLoggerResponse) String() string { func (x *RestartLoggerResponse) String() string {
@@ -113,7 +124,7 @@ func (*RestartLoggerResponse) ProtoMessage() {}
func (x *RestartLoggerResponse) ProtoReflect() protoreflect.Message { func (x *RestartLoggerResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_log_command_config_proto_msgTypes[2] mi := &file_app_log_command_config_proto_msgTypes[2]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -168,7 +179,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_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 (*Config)(nil), // 0: xray.app.log.command.Config
(*RestartLoggerRequest)(nil), // 1: xray.app.log.command.RestartLoggerRequest (*RestartLoggerRequest)(nil), // 1: xray.app.log.command.RestartLoggerRequest
(*RestartLoggerResponse)(nil), // 2: xray.app.log.command.RestartLoggerResponse (*RestartLoggerResponse)(nil), // 2: xray.app.log.command.RestartLoggerResponse
@@ -188,6 +199,44 @@ func file_app_log_command_config_proto_init() {
if File_app_log_command_config_proto != nil { if File_app_log_command_config_proto != nil {
return 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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{

View File

@@ -1,8 +1,4 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// source: app/log/command/config.proto
package command package command
@@ -15,12 +11,8 @@ import (
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against. // is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later. // Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion9 const _ = grpc.SupportPackageIsVersion7
const (
LoggerService_RestartLogger_FullMethodName = "/xray.app.log.command.LoggerService/RestartLogger"
)
// LoggerServiceClient is the client API for LoggerService service. // LoggerServiceClient is the client API for LoggerService service.
// //
@@ -38,9 +30,8 @@ func NewLoggerServiceClient(cc grpc.ClientConnInterface) LoggerServiceClient {
} }
func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) { func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RestartLoggerResponse) 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 { if err != nil {
return nil, err return nil, err
} }
@@ -49,24 +40,20 @@ func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLogg
// LoggerServiceServer is the server API for LoggerService service. // LoggerServiceServer is the server API for LoggerService service.
// All implementations must embed UnimplementedLoggerServiceServer // All implementations must embed UnimplementedLoggerServiceServer
// for forward compatibility. // for forward compatibility
type LoggerServiceServer interface { type LoggerServiceServer interface {
RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error)
mustEmbedUnimplementedLoggerServiceServer() mustEmbedUnimplementedLoggerServiceServer()
} }
// UnimplementedLoggerServiceServer must be embedded to have // UnimplementedLoggerServiceServer must be embedded to have forward compatible implementations.
// forward compatible implementations. type UnimplementedLoggerServiceServer struct {
// }
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedLoggerServiceServer struct{}
func (UnimplementedLoggerServiceServer) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) { func (UnimplementedLoggerServiceServer) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RestartLogger not implemented") return nil, status.Errorf(codes.Unimplemented, "method RestartLogger not implemented")
} }
func (UnimplementedLoggerServiceServer) mustEmbedUnimplementedLoggerServiceServer() {} func (UnimplementedLoggerServiceServer) mustEmbedUnimplementedLoggerServiceServer() {}
func (UnimplementedLoggerServiceServer) testEmbeddedByValue() {}
// UnsafeLoggerServiceServer may be embedded to opt out of forward compatibility for this service. // 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 // Use of this interface is not recommended, as added methods to LoggerServiceServer will
@@ -76,13 +63,6 @@ type UnsafeLoggerServiceServer interface {
} }
func RegisterLoggerServiceServer(s grpc.ServiceRegistrar, srv LoggerServiceServer) { 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) s.RegisterService(&LoggerService_ServiceDesc, srv)
} }
@@ -96,7 +76,7 @@ func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: LoggerService_RestartLogger_FullMethodName, FullMethod: "/xray.app.log.command.LoggerService/RestartLogger",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest)) return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest))

View File

@@ -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{})
}

View File

@@ -1,12 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.25.0
// protoc v5.28.2 // protoc v3.14.0
// source: app/log/config.proto // source: app/log/config.proto
package log package log
import ( import (
proto "github.com/golang/protobuf/proto"
log "github.com/xtls/xray-core/common/log" log "github.com/xtls/xray-core/common/log"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -21,6 +22,10 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type LogType int32 type LogType int32
const ( const (
@@ -84,14 +89,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"` 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"` 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"` EnableDnsLog bool `protobuf:"varint,6,opt,name=enable_dns_log,json=enableDnsLog,proto3" json:"enable_dns_log,omitempty"`
MaskAddress string `protobuf:"bytes,7,opt,name=mask_address,json=maskAddress,proto3" json:"mask_address,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
*x = Config{} *x = Config{}
mi := &file_app_log_config_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_log_config_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Config) String() string { func (x *Config) String() string {
@@ -102,7 +108,7 @@ func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message { func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_log_config_proto_msgTypes[0] mi := &file_app_log_config_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -128,7 +134,7 @@ func (x *Config) GetErrorLogLevel() log.Severity {
if x != nil { if x != nil {
return x.ErrorLogLevel return x.ErrorLogLevel
} }
return log.Severity(0) return log.Severity_Unknown
} }
func (x *Config) GetErrorLogPath() string { func (x *Config) GetErrorLogPath() string {
@@ -159,20 +165,13 @@ func (x *Config) GetEnableDnsLog() bool {
return false 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 protoreflect.FileDescriptor
var file_app_log_config_proto_rawDesc = []byte{ var file_app_log_config_proto_rawDesc = []byte{
0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 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, 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, 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, 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, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67,
@@ -192,18 +191,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, 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, 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, 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, 0x6c, 0x65, 0x44, 0x6e, 0x73, 0x4c, 0x6f, 0x67, 0x2a, 0x35, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x54,
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a,
0x6d, 0x61, 0x73, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2a, 0x35, 0x0a, 0x07, 0x4c, 0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x69,
0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x10, 0x03, 0x42,
0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x04, 0x46, 0x69, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x6c, 0x6f, 0x67, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72,
0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
} }
var ( var (
@@ -220,7 +216,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_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_app_log_config_proto_msgTypes = make([]protoimpl.MessageInfo, 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 (LogType)(0), // 0: xray.app.log.LogType
(*Config)(nil), // 1: xray.app.log.Config (*Config)(nil), // 1: xray.app.log.Config
(log.Severity)(0), // 2: xray.common.log.Severity (log.Severity)(0), // 2: xray.common.log.Severity
@@ -241,6 +237,20 @@ func file_app_log_config_proto_init() {
if File_app_log_config_proto != nil { if File_app_log_config_proto != nil {
return 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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{

View File

@@ -23,5 +23,4 @@ message Config {
LogType access_log_type = 4; LogType access_log_type = 4;
string access_log_path = 5; string access_log_path = 5;
bool enable_dns_log = 6; bool enable_dns_log = 6;
string mask_address= 7;
} }

View File

@@ -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{})
}

View File

@@ -1,14 +1,12 @@
package log package log
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
import ( import (
"context" "context"
"fmt"
"regexp"
"strings"
"sync" "sync"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
) )
@@ -31,13 +29,13 @@ func New(ctx context.Context, config *Config) (*Instance, error) {
} }
log.RegisterHandler(g) log.RegisterHandler(g)
// start logger now, // start logger instantly on inited
// then other modules will be able to log during initialization // other modules would log during init
if err := g.startInternal(); err != nil { if err := g.startInternal(); err != nil {
return nil, err return nil, err
} }
errors.LogDebug(ctx, "Logger started") newError("Logger started").AtDebug().WriteToLog()
return g, nil return g, nil
} }
@@ -79,10 +77,10 @@ func (g *Instance) startInternal() error {
g.active = true g.active = true
if err := g.initAccessLogger(); err != nil { 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 { 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 return nil
@@ -102,25 +100,18 @@ func (g *Instance) Handle(msg log.Message) {
return return
} }
var Msg log.Message
if g.config.MaskAddress != "" {
Msg = &MaskedMsgWrapper{Message: msg, config: g.config}
} else {
Msg = msg
}
switch msg := msg.(type) { switch msg := msg.(type) {
case *log.AccessMessage: case *log.AccessMessage:
if g.accessLogger != nil { if g.accessLogger != nil {
g.accessLogger.Handle(Msg) g.accessLogger.Handle(msg)
} }
case *log.DNSLog: case *log.DNSLog:
if g.dns && g.accessLogger != nil { if g.dns && g.accessLogger != nil {
g.accessLogger.Handle(Msg) g.accessLogger.Handle(msg)
} }
case *log.GeneralMessage: case *log.GeneralMessage:
if g.errorLogger != nil && msg.Severity <= g.config.ErrorLogLevel { if g.errorLogger != nil && msg.Severity <= g.config.ErrorLogLevel {
g.errorLogger.Handle(Msg) g.errorLogger.Handle(msg)
} }
default: default:
// Swallow // Swallow
@@ -129,7 +120,7 @@ func (g *Instance) Handle(msg log.Message) {
// Close implements common.Closable.Close(). // Close implements common.Closable.Close().
func (g *Instance) Close() error { func (g *Instance) Close() error {
errors.LogDebug(context.Background(), "Logger closing") newError("Logger closing").AtDebug().WriteToLog()
g.Lock() g.Lock()
defer g.Unlock() defer g.Unlock()
@@ -149,56 +140,6 @@ func (g *Instance) Close() error {
return nil 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() { func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config)) return New(ctx, config.(*Config))

View File

@@ -1,10 +1,7 @@
package log package log
import ( import (
"sync"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
) )
@@ -14,29 +11,23 @@ type HandlerCreatorOptions struct {
type HandlerCreator func(LogType, HandlerCreatorOptions) (log.Handler, error) type HandlerCreator func(LogType, HandlerCreatorOptions) (log.Handler, error)
var handlerCreatorMap = make(map[LogType]HandlerCreator) var (
handlerCreatorMap = make(map[LogType]HandlerCreator)
var handlerCreatorMapLock = &sync.RWMutex{} )
func RegisterHandlerCreator(logType LogType, f HandlerCreator) error { func RegisterHandlerCreator(logType LogType, f HandlerCreator) error {
if f == nil { if f == nil {
return errors.New("nil HandlerCreator") return newError("nil HandlerCreator")
} }
handlerCreatorMapLock.Lock()
defer handlerCreatorMapLock.Unlock()
handlerCreatorMap[logType] = f handlerCreatorMap[logType] = f
return nil return nil
} }
func createHandler(logType LogType, options HandlerCreatorOptions) (log.Handler, error) { func createHandler(logType LogType, options HandlerCreatorOptions) (log.Handler, error) {
handlerCreatorMapLock.RLock()
defer handlerCreatorMapLock.RUnlock()
creator, found := handlerCreatorMap[logType] creator, found := handlerCreatorMap[logType]
if !found { 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) return creator(logType, options)
} }

View File

@@ -1,142 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// source: app/metrics/config.proto
package metrics
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Config is the settings for metrics.
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Tag of the outbound handler that handles metrics http connections.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Listen string `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,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)
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_metrics_config_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_metrics_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetTag() string {
if x != nil {
return x.Tag
}
return ""
}
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,
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,
}
var (
file_app_metrics_config_proto_rawDescOnce sync.Once
file_app_metrics_config_proto_rawDescData = file_app_metrics_config_proto_rawDesc
)
func file_app_metrics_config_proto_rawDescGZIP() []byte {
file_app_metrics_config_proto_rawDescOnce.Do(func() {
file_app_metrics_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_metrics_config_proto_rawDescData)
})
return file_app_metrics_config_proto_rawDescData
}
var file_app_metrics_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_app_metrics_config_proto_goTypes = []any{
(*Config)(nil), // 0: xray.app.metrics.Config
}
var file_app_metrics_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_app_metrics_config_proto_init() }
func file_app_metrics_config_proto_init() {
if File_app_metrics_config_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_metrics_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_app_metrics_config_proto_goTypes,
DependencyIndexes: file_app_metrics_config_proto_depIdxs,
MessageInfos: file_app_metrics_config_proto_msgTypes,
}.Build()
File_app_metrics_config_proto = out.File
file_app_metrics_config_proto_rawDesc = nil
file_app_metrics_config_proto_goTypes = nil
file_app_metrics_config_proto_depIdxs = nil
}

View File

@@ -1,14 +0,0 @@
syntax = "proto3";
package xray.app.metrics;
option csharp_namespace = "Xray.App.Metrics";
option go_package = "github.com/xtls/xray-core/app/metrics";
option java_package = "com.xray.app.metrics";
option java_multiple_files = true;
// Config is the settings for metrics.
message Config {
// Tag of the outbound handler that handles metrics http connections.
string tag = 1;
string listen = 2;
}

View File

@@ -1,139 +0,0 @@
package metrics
import (
"context"
"expvar"
"net/http"
_ "net/http/pprof"
"strings"
"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension"
"github.com/xtls/xray-core/features/outbound"
feature_stats "github.com/xtls/xray-core/features/stats"
)
type MetricsHandler struct {
ohm outbound.Manager
statsManager feature_stats.Manager
observatory extension.Observatory
tag string
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,
}
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) {
c.statsManager = sm
c.ohm = om
}))
expvar.Publish("stats", expvar.Func(func() interface{} {
manager, ok := c.statsManager.(*stats.Manager)
if !ok {
return nil
}
resp := map[string]map[string]map[string]int64{
"inbound": {},
"outbound": {},
"user": {},
}
manager.VisitCounters(func(name string, counter feature_stats.Counter) bool {
nameSplit := strings.Split(name, ">>>")
typeName, tagOrUser, direction := nameSplit[0], nameSplit[1], nameSplit[3]
if item, found := resp[typeName][tagOrUser]; found {
item[direction] = counter.Value()
} else {
resp[typeName][tagOrUser] = map[string]int64{
direction: counter.Value(),
}
}
return true
})
return resp
}))
expvar.Publish("observatory", expvar.Func(func() interface{} {
if c.observatory == nil {
common.Must(core.RequireFeatures(ctx, func(observatory extension.Observatory) error {
c.observatory = observatory
return nil
}))
if c.observatory == nil {
return nil
}
}
resp := map[string]*observatory.OutboundStatus{}
if o, err := c.observatory.GetObservation(context.Background()); err != nil {
return err
} else {
for _, x := range o.(*observatory.ObservationResult).GetStatus() {
resp[x.OutboundTag] = x
}
}
return resp
}))
return c, nil
}
func (p *MetricsHandler) Type() interface{} {
return (*MetricsHandler)(nil)
}
func (p *MetricsHandler) Start() error {
// 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(),
}
go func() {
if err := http.Serve(listener, http.DefaultServeMux); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
}
}()
if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil {
errors.LogInfo(context.Background(), "failed to remove existing handler")
}
return p.ohm.AddHandler(context.Background(), &Outbound{
tag: p.tag,
listener: listener,
})
}
func (p *MetricsHandler) Close() error {
return nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
return NewMetricsHandler(ctx, cfg.(*Config))
}))
}

View File

@@ -1,121 +0,0 @@
package metrics
import (
"context"
"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"
)
// OutboundListener is a net.Listener for listening metrics http connections.
type OutboundListener struct {
buffer chan net.Conn
done *done.Instance
}
func (l *OutboundListener) add(conn net.Conn) {
select {
case l.buffer <- conn:
case <-l.done.Wait():
conn.Close()
default:
conn.Close()
}
}
// Accept implements net.Listener.
func (l *OutboundListener) Accept() (net.Conn, error) {
select {
case <-l.done.Wait():
return nil, errors.New("listen closed")
case c := <-l.buffer:
return c, nil
}
}
// Close implement net.Listener.
func (l *OutboundListener) Close() error {
common.Must(l.done.Close())
L:
for {
select {
case c := <-l.buffer:
c.Close()
default:
break L
}
}
return nil
}
// Addr implements net.Listener.
func (l *OutboundListener) Addr() net.Addr {
return &net.TCPAddr{
IP: net.IP{0, 0, 0, 0},
Port: 0,
}
}
// Outbound is an outbound.Handler that handles metrics http connections.
type Outbound struct {
tag string
listener *OutboundListener
access sync.RWMutex
closed bool
}
// Dispatch implements outbound.Handler.
func (co *Outbound) Dispatch(ctx context.Context, link *transport.Link) {
co.access.RLock()
if co.closed {
common.Interrupt(link.Reader)
common.Interrupt(link.Writer)
co.access.RUnlock()
return
}
closeSignal := done.New()
c := cnc.NewConnection(cnc.ConnectionInputMulti(link.Writer), cnc.ConnectionOutputMulti(link.Reader), cnc.ConnectionOnClose(closeSignal))
co.listener.add(c)
co.access.RUnlock()
<-closeSignal.Wait()
}
// Tag implements outbound.Handler.
func (co *Outbound) Tag() string {
return co.tag
}
// Start implements common.Runnable.
func (co *Outbound) Start() error {
co.access.Lock()
co.closed = false
co.access.Unlock()
return nil
}
// Close implements common.Closable.
func (co *Outbound) Close() error {
co.access.Lock()
defer co.access.Unlock()
co.closed = true
return co.listener.Close()
}
// SenderSettings implements outbound.Handler.
func (co *Outbound) SenderSettings() *serial.TypedMessage {
return nil
}
// ProxySettings implements outbound.Handler.
func (co *Outbound) ProxySettings() *serial.TypedMessage {
return nil
}

View File

@@ -1,12 +0,0 @@
package burst
import (
"math"
"time"
)
const (
rttFailed = time.Duration(math.MaxInt64 - iota)
rttUntested
rttUnqualified
)

View File

@@ -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))
}))
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -1,47 +0,0 @@
package command
import (
"context"
"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/common"
core "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension"
"google.golang.org/grpc"
)
type service struct {
UnimplementedObservatoryServiceServer
v *core.Instance
observatory extension.Observatory
}
func (s *service) GetOutboundStatus(ctx context.Context, request *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) {
resp, err := s.observatory.GetObservation(ctx)
if err != nil {
return nil, err
}
retdata := resp.(*observatory.ObservationResult)
return &GetOutboundStatusResponse{
Status: retdata,
}, nil
}
func (s *service) Register(server *grpc.Server) {
RegisterObservatoryServiceServer(server, s)
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
s := core.MustFromContext(ctx)
sv := &service{v: s}
err := s.RequireFeatures(func(Observatory extension.Observatory) {
sv.observatory = Observatory
}, false)
if err != nil {
return nil, err
}
return sv, nil
}))
}

View File

@@ -1,234 +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/command/command.proto
package command
import (
observatory "github.com/xtls/xray-core/app/observatory"
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 GetOutboundStatusRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
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)
}
func (x *GetOutboundStatusRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetOutboundStatusRequest) ProtoMessage() {}
func (x *GetOutboundStatusRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_observatory_command_command_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 GetOutboundStatusRequest.ProtoReflect.Descriptor instead.
func (*GetOutboundStatusRequest) Descriptor() ([]byte, []int) {
return file_app_observatory_command_command_proto_rawDescGZIP(), []int{0}
}
type GetOutboundStatusResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Status *observatory.ObservationResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
}
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)
}
func (x *GetOutboundStatusResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetOutboundStatusResponse) ProtoMessage() {}
func (x *GetOutboundStatusResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_observatory_command_command_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 GetOutboundStatusResponse.ProtoReflect.Descriptor instead.
func (*GetOutboundStatusResponse) Descriptor() ([]byte, []int) {
return file_app_observatory_command_command_proto_rawDescGZIP(), []int{1}
}
func (x *GetOutboundStatusResponse) GetStatus() *observatory.ObservationResult {
if x != nil {
return x.Status
}
return nil
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
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)
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_observatory_command_command_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_app_observatory_command_command_proto_rawDescGZIP(), []int{2}
}
var File_app_observatory_command_command_proto protoreflect.FileDescriptor
var file_app_observatory_command_command_proto_rawDesc = []byte{
0x0a, 0x25, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x1c, 0x61, 0x70, 0x70, 0x2f,
0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4f,
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x22, 0x61, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x62,
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x32, 0xa7, 0x01, 0x0a, 0x12, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x90, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74,
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3b,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65,
0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x80, 0x01, 0x0a, 0x25,
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61,
0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72,
0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_app_observatory_command_command_proto_rawDescOnce sync.Once
file_app_observatory_command_command_proto_rawDescData = file_app_observatory_command_command_proto_rawDesc
)
func file_app_observatory_command_command_proto_rawDescGZIP() []byte {
file_app_observatory_command_command_proto_rawDescOnce.Do(func() {
file_app_observatory_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_command_command_proto_rawDescData)
})
return file_app_observatory_command_command_proto_rawDescData
}
var file_app_observatory_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_app_observatory_command_command_proto_goTypes = []any{
(*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
(*observatory.ObservationResult)(nil), // 3: xray.core.app.observatory.ObservationResult
}
var file_app_observatory_command_command_proto_depIdxs = []int32{
3, // 0: xray.core.app.observatory.command.GetOutboundStatusResponse.status:type_name -> xray.core.app.observatory.ObservationResult
0, // 1: xray.core.app.observatory.command.ObservatoryService.GetOutboundStatus:input_type -> xray.core.app.observatory.command.GetOutboundStatusRequest
1, // 2: xray.core.app.observatory.command.ObservatoryService.GetOutboundStatus:output_type -> xray.core.app.observatory.command.GetOutboundStatusResponse
2, // [2:3] is the sub-list for method output_type
1, // [1:2] 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_command_command_proto_init() }
func file_app_observatory_command_command_proto_init() {
if File_app_observatory_command_command_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_observatory_command_command_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_app_observatory_command_command_proto_goTypes,
DependencyIndexes: file_app_observatory_command_command_proto_depIdxs,
MessageInfos: file_app_observatory_command_command_proto_msgTypes,
}.Build()
File_app_observatory_command_command_proto = out.File
file_app_observatory_command_command_proto_rawDesc = nil
file_app_observatory_command_command_proto_goTypes = nil
file_app_observatory_command_command_proto_depIdxs = nil
}

View File

@@ -1,24 +0,0 @@
syntax = "proto3";
package xray.core.app.observatory.command;
option csharp_namespace = "Xray.Core.App.Observatory.Command";
option go_package = "github.com/xtls/xray-core/app/observatory/command";
option java_package = "com.xray.core.app.observatory.command";
option java_multiple_files = true;
import "app/observatory/config.proto";
message GetOutboundStatusRequest {
}
message GetOutboundStatusResponse {
xray.core.app.observatory.ObservationResult status = 1;
}
service ObservatoryService {
rpc GetOutboundStatus(GetOutboundStatusRequest)
returns (GetOutboundStatusResponse) {}
}
message Config {}

View File

@@ -1,121 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// source: app/observatory/command/command.proto
package command
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// 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"
)
// ObservatoryServiceClient is the client API for ObservatoryService 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 ObservatoryServiceClient interface {
GetOutboundStatus(ctx context.Context, in *GetOutboundStatusRequest, opts ...grpc.CallOption) (*GetOutboundStatusResponse, error)
}
type observatoryServiceClient struct {
cc grpc.ClientConnInterface
}
func NewObservatoryServiceClient(cc grpc.ClientConnInterface) ObservatoryServiceClient {
return &observatoryServiceClient{cc}
}
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...)
if err != nil {
return nil, err
}
return out, nil
}
// ObservatoryServiceServer is the server API for ObservatoryService service.
// All implementations must embed UnimplementedObservatoryServiceServer
// 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{}
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
// result in compilation errors.
type UnsafeObservatoryServiceServer interface {
mustEmbedUnimplementedObservatoryServiceServer()
}
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)
}
func _ObservatoryService_GetOutboundStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetOutboundStatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ObservatoryServiceServer).GetOutboundStatus(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ObservatoryService_GetOutboundStatus_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ObservatoryServiceServer).GetOutboundStatus(ctx, req.(*GetOutboundStatusRequest))
}
return interceptor(ctx, in, info, handler)
}
// ObservatoryService_ServiceDesc is the grpc.ServiceDesc for ObservatoryService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ObservatoryService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "xray.core.app.observatory.command.ObservatoryService",
HandlerType: (*ObservatoryServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetOutboundStatus",
Handler: _ObservatoryService_GetOutboundStatus_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "app/observatory/command/command.proto",
}

View File

@@ -1,568 +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/config.proto
package observatory
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 ObservationResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Status []*OutboundStatus `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"`
}
func (x *ObservationResult) Reset() {
*x = ObservationResult{}
mi := &file_app_observatory_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ObservationResult) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ObservationResult) ProtoMessage() {}
func (x *ObservationResult) ProtoReflect() protoreflect.Message {
mi := &file_app_observatory_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 ObservationResult.ProtoReflect.Descriptor instead.
func (*ObservationResult) Descriptor() ([]byte, []int) {
return file_app_observatory_config_proto_rawDescGZIP(), []int{0}
}
func (x *ObservationResult) GetStatus() []*OutboundStatus {
if x != nil {
return x.Status
}
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
unknownFields protoimpl.UnknownFields
// @Document Whether this outbound is usable
// @Restriction ReadOnlyForUser
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
// @Document The time for probe request to finish.
// @Type time.ms
// @Restriction ReadOnlyForUser
Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"`
// @Document The last error caused this outbound failed to relay probe request
// @Restriction NotMachineReadable
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
// @Document The outbound tag for this Server
// @Type id.outboundTag
OutboundTag string `protobuf:"bytes,4,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"`
// @Document The time this outbound is known to be alive
// @Type id.outboundTag
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"`
}
func (x *OutboundStatus) Reset() {
*x = OutboundStatus{}
mi := &file_app_observatory_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *OutboundStatus) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OutboundStatus) ProtoMessage() {}
func (x *OutboundStatus) ProtoReflect() protoreflect.Message {
mi := &file_app_observatory_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 OutboundStatus.ProtoReflect.Descriptor instead.
func (*OutboundStatus) Descriptor() ([]byte, []int) {
return file_app_observatory_config_proto_rawDescGZIP(), []int{2}
}
func (x *OutboundStatus) GetAlive() bool {
if x != nil {
return x.Alive
}
return false
}
func (x *OutboundStatus) GetDelay() int64 {
if x != nil {
return x.Delay
}
return 0
}
func (x *OutboundStatus) GetLastErrorReason() string {
if x != nil {
return x.LastErrorReason
}
return ""
}
func (x *OutboundStatus) GetOutboundTag() string {
if x != nil {
return x.OutboundTag
}
return ""
}
func (x *OutboundStatus) GetLastSeenTime() int64 {
if x != nil {
return x.LastSeenTime
}
return 0
}
func (x *OutboundStatus) GetLastTryTime() int64 {
if x != nil {
return x.LastTryTime
}
return 0
}
func (x *OutboundStatus) GetHealthPing() *HealthPingMeasurementResult {
if x != nil {
return x.HealthPing
}
return nil
}
type ProbeResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @Document Whether this outbound is usable
// @Restriction ReadOnlyForUser
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
// @Document The time for probe request to finish.
// @Type time.ms
// @Restriction ReadOnlyForUser
Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"`
// @Document The error caused this outbound failed to relay probe request
// @Restriction NotMachineReadable
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
}
func (x *ProbeResult) Reset() {
*x = ProbeResult{}
mi := &file_app_observatory_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ProbeResult) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ProbeResult) ProtoMessage() {}
func (x *ProbeResult) ProtoReflect() protoreflect.Message {
mi := &file_app_observatory_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 ProbeResult.ProtoReflect.Descriptor instead.
func (*ProbeResult) Descriptor() ([]byte, []int) {
return file_app_observatory_config_proto_rawDescGZIP(), []int{3}
}
func (x *ProbeResult) GetAlive() bool {
if x != nil {
return x.Alive
}
return false
}
func (x *ProbeResult) GetDelay() int64 {
if x != nil {
return x.Delay
}
return 0
}
func (x *ProbeResult) GetLastErrorReason() string {
if x != nil {
return x.LastErrorReason
}
return ""
}
type Intensity struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @Document The time interval for a probe request in ms.
// @Type time.ms
ProbeInterval uint32 `protobuf:"varint,1,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
}
func (x *Intensity) Reset() {
*x = Intensity{}
mi := &file_app_observatory_config_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Intensity) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Intensity) ProtoMessage() {}
func (x *Intensity) ProtoReflect() protoreflect.Message {
mi := &file_app_observatory_config_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Intensity.ProtoReflect.Descriptor instead.
func (*Intensity) Descriptor() ([]byte, []int) {
return file_app_observatory_config_proto_rawDescGZIP(), []int{4}
}
func (x *Intensity) GetProbeInterval() uint32 {
if x != nil {
return x.ProbeInterval
}
return 0
}
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"`
ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"`
ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
EnableConcurrency bool `protobuf:"varint,5,opt,name=enable_concurrency,json=enableConcurrency,proto3" json:"enable_concurrency,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_observatory_config_proto_msgTypes[5]
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_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 Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_observatory_config_proto_rawDescGZIP(), []int{5}
}
func (x *Config) GetSubjectSelector() []string {
if x != nil {
return x.SubjectSelector
}
return nil
}
func (x *Config) GetProbeUrl() string {
if x != nil {
return x.ProbeUrl
}
return ""
}
func (x *Config) GetProbeInterval() int64 {
if x != nil {
return x.ProbeInterval
}
return 0
}
func (x *Config) GetEnableConcurrency() bool {
if x != nil {
return x.EnableConcurrency
}
return false
}
var File_app_observatory_config_proto protoreflect.FileDescriptor
var file_app_observatory_config_proto_rawDesc = []byte{
0x0a, 0x1c, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19,
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, 0x22, 0x56, 0x0a, 0x11, 0x4f, 0x62, 0x73,
0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x41,
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29,
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,
}
var (
file_app_observatory_config_proto_rawDescOnce sync.Once
file_app_observatory_config_proto_rawDescData = file_app_observatory_config_proto_rawDesc
)
func file_app_observatory_config_proto_rawDescGZIP() []byte {
file_app_observatory_config_proto_rawDescOnce.Do(func() {
file_app_observatory_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_config_proto_rawDescData)
})
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_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
}
func init() { file_app_observatory_config_proto_init() }
func file_app_observatory_config_proto_init() {
if File_app_observatory_config_proto != nil {
return
}
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,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_app_observatory_config_proto_goTypes,
DependencyIndexes: file_app_observatory_config_proto_depIdxs,
MessageInfos: file_app_observatory_config_proto_msgTypes,
}.Build()
File_app_observatory_config_proto = out.File
file_app_observatory_config_proto_rawDesc = nil
file_app_observatory_config_proto_goTypes = nil
file_app_observatory_config_proto_depIdxs = nil
}

View File

@@ -1,84 +0,0 @@
syntax = "proto3";
package xray.core.app.observatory;
option csharp_namespace = "Xray.App.Observatory";
option go_package = "github.com/xtls/xray-core/app/observatory";
option java_package = "com.xray.app.observatory";
option java_multiple_files = true;
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
*/
bool alive = 1;
/* @Document The time for probe request to finish.
@Type time.ms
@Restriction ReadOnlyForUser
*/
int64 delay = 2;
/* @Document The last error caused this outbound failed to relay probe request
@Restriction NotMachineReadable
*/
string last_error_reason = 3;
/* @Document The outbound tag for this Server
@Type id.outboundTag
*/
string outbound_tag = 4;
/* @Document The time this outbound is known to be alive
@Type id.outboundTag
*/
int64 last_seen_time = 5;
/* @Document The time this outbound is tried
@Type id.outboundTag
*/
int64 last_try_time = 6;
HealthPingMeasurementResult health_ping = 7;
}
message ProbeResult{
/* @Document Whether this outbound is usable
@Restriction ReadOnlyForUser
*/
bool alive = 1;
/* @Document The time for probe request to finish.
@Type time.ms
@Restriction ReadOnlyForUser
*/
int64 delay = 2;
/* @Document The error caused this outbound failed to relay probe request
@Restriction NotMachineReadable
*/
string last_error_reason = 3;
}
message Intensity{
/* @Document The time interval for a probe request in ms.
@Type time.ms
*/
uint32 probe_interval = 1;
}
message Config {
/* @Document The selectors for outbound under observation
*/
repeated string subject_selector = 2;
string probe_url = 3;
int64 probe_interval = 4;
bool enable_concurrency = 5;
}

View File

@@ -1,26 +0,0 @@
package observatory
import "github.com/xtls/xray-core/common/errors"
type errorCollector struct {
errors *errors.Error
}
func (e *errorCollector) SubmitError(err error) {
if e.errors == nil {
e.errors = errors.New("underlying connection error").Base(err)
return
}
e.errors = e.errors.Base(errors.New("underlying connection error").Base(err))
}
func newErrorCollector() *errorCollector {
return &errorCollector{}
}
func (e *errorCollector) UnderlyingError() error {
if e.errors == nil {
return errors.New("failed to produce report")
}
return e.errors
}

View File

@@ -1 +0,0 @@
package observatory

View File

@@ -1,240 +0,0 @@
package observatory
import (
"context"
"net"
"net/http"
"net/url"
"sort"
"sync"
"time"
"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"
"github.com/xtls/xray-core/common/task"
"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 {
config *Config
ctx context.Context
statusLock sync.Mutex
status []*OutboundStatus
finished *done.Instance
ohm outbound.Manager
dispatcher routing.Dispatcher
}
func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
return &ObservationResult{Status: o.status}, nil
}
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()
go o.background()
}
return nil
}
func (o *Observer) Close() error {
if o.finished != nil {
return o.finished.Close()
}
return nil
}
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")
return
}
outbounds := hs.Select(o.config.SubjectSelector)
o.updateStatus(outbounds)
sleepTime := time.Second * 10
if o.config.ProbeInterval != 0 {
sleepTime = time.Duration(o.config.ProbeInterval)
}
if !o.config.EnableConcurrency {
sort.Strings(outbounds)
for _, v := range outbounds {
result := o.probe(v)
o.updateStatusForResult(v, &result)
if o.finished.Done() {
return
}
time.Sleep(sleepTime)
}
continue
}
ch := make(chan struct{}, len(outbounds))
for _, v := range outbounds {
go func(v string) {
result := o.probe(v)
o.updateStatusForResult(v, &result)
ch <- struct{}{}
}(v)
}
for range outbounds {
select {
case <-ch:
case <-o.finished.Wait():
return
}
}
time.Sleep(sleepTime)
}
}
func (o *Observer) updateStatus(outbounds []string) {
o.statusLock.Lock()
defer o.statusLock.Unlock()
// TODO should remove old inbound that is removed
_ = outbounds
}
func (o *Observer) probe(outbound string) ProbeResult {
errorCollectorForRequest := newErrorCollector()
httpTransport := http.Transport{
Proxy: func(*http.Request) (*url.URL, error) {
return nil, nil
},
DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
var connection net.Conn
taskErr := task.Run(ctx, func() error {
// 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)
}
trackedCtx := session.TrackedConnectionError(o.ctx, errorCollectorForRequest)
conn, err := tagged.Dialer(trackedCtx, o.dispatcher, dest, outbound)
if err != nil {
return errors.New("cannot dial remote address ", dest).Base(err)
}
connection = conn
return nil
})
if taskErr != nil {
return nil, errors.New("cannot finish connection").Base(taskErr)
}
return connection, nil
},
TLSHandshakeTimeout: time.Second * 5,
}
httpClient := &http.Client{
Transport: &httpTransport,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Jar: nil,
Timeout: time.Second * 5,
}
var GETTime time.Duration
err := task.Run(o.ctx, func() error {
startTime := time.Now()
probeURL := "https://www.google.com/generate_204"
if o.config.ProbeUrl != "" {
probeURL = o.config.ProbeUrl
}
response, err := httpClient.Get(probeURL)
if err != nil {
return errors.New("outbound failed to relay connection").Base(err)
}
if response.Body != nil {
response.Body.Close()
}
endTime := time.Now()
GETTime = endTime.Sub(startTime)
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}
}
errors.LogInfo(o.ctx, "the outbound ", outbound, " is alive:", GETTime.Seconds())
return ProbeResult{Alive: true, Delay: GETTime.Milliseconds()}
}
func (o *Observer) updateStatusForResult(outbound string, result *ProbeResult) {
o.statusLock.Lock()
defer o.statusLock.Unlock()
var status *OutboundStatus
if location := o.findStatusLocationLockHolderOnly(outbound); location != -1 {
status = o.status[location]
} else {
status = &OutboundStatus{}
o.status = append(o.status, status)
}
status.LastTryTime = time.Now().Unix()
status.OutboundTag = outbound
status.Alive = result.Alive
if result.Alive {
status.Delay = result.Delay
status.LastSeenTime = status.LastTryTime
status.LastErrorReason = ""
} else {
status.LastErrorReason = result.LastErrorReason
status.Delay = 99999999
}
}
func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int {
for i, v := range o.status {
if v.OutboundTag == outbound {
return i
}
}
return -1
}
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)
}
return &Observer{
config: config,
ctx: ctx,
ohm: outboundManager,
dispatcher: dispatcher,
}, nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config))
}))
}

View File

@@ -73,7 +73,6 @@ func (p *Policy) ToCorePolicy() policy.Session {
if p.Stats != nil { if p.Stats != nil {
cp.Stats.UserUplink = p.Stats.UserUplink cp.Stats.UserUplink = p.Stats.UserUplink
cp.Stats.UserDownlink = p.Stats.UserDownlink cp.Stats.UserDownlink = p.Stats.UserDownlink
cp.Stats.UserOnline = p.Stats.UserOnline
} }
if p.Buffer != nil { if p.Buffer != nil {
cp.Buffer.PerConnection = p.Buffer.Connection cp.Buffer.PerConnection = p.Buffer.Connection

View File

@@ -1,12 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.25.0
// protoc v5.28.2 // protoc v3.14.0
// source: app/policy/config.proto // source: app/policy/config.proto
package policy package policy
import ( import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
@@ -20,6 +21,10 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Second struct { type Second struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -30,9 +35,11 @@ type Second struct {
func (x *Second) Reset() { func (x *Second) Reset() {
*x = Second{} *x = Second{}
mi := &file_app_policy_config_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Second) String() string { func (x *Second) String() string {
@@ -43,7 +50,7 @@ func (*Second) ProtoMessage() {}
func (x *Second) ProtoReflect() protoreflect.Message { func (x *Second) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[0] mi := &file_app_policy_config_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -77,9 +84,11 @@ type Policy struct {
func (x *Policy) Reset() { func (x *Policy) Reset() {
*x = Policy{} *x = Policy{}
mi := &file_app_policy_config_proto_msgTypes[1] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[1]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Policy) String() string { func (x *Policy) String() string {
@@ -90,7 +99,7 @@ func (*Policy) ProtoMessage() {}
func (x *Policy) ProtoReflect() protoreflect.Message { func (x *Policy) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[1] mi := &file_app_policy_config_proto_msgTypes[1]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -136,9 +145,11 @@ type SystemPolicy struct {
func (x *SystemPolicy) Reset() { func (x *SystemPolicy) Reset() {
*x = SystemPolicy{} *x = SystemPolicy{}
mi := &file_app_policy_config_proto_msgTypes[2] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[2]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *SystemPolicy) String() string { func (x *SystemPolicy) String() string {
@@ -149,7 +160,7 @@ func (*SystemPolicy) ProtoMessage() {}
func (x *SystemPolicy) ProtoReflect() protoreflect.Message { func (x *SystemPolicy) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[2] mi := &file_app_policy_config_proto_msgTypes[2]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -182,9 +193,11 @@ type Config struct {
func (x *Config) Reset() { func (x *Config) Reset() {
*x = Config{} *x = Config{}
mi := &file_app_policy_config_proto_msgTypes[3] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[3]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Config) String() string { func (x *Config) String() string {
@@ -195,7 +208,7 @@ func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message { func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[3] mi := &file_app_policy_config_proto_msgTypes[3]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -238,9 +251,11 @@ type Policy_Timeout struct {
func (x *Policy_Timeout) Reset() { func (x *Policy_Timeout) Reset() {
*x = Policy_Timeout{} *x = Policy_Timeout{}
mi := &file_app_policy_config_proto_msgTypes[4] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[4]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Policy_Timeout) String() string { func (x *Policy_Timeout) String() string {
@@ -251,7 +266,7 @@ func (*Policy_Timeout) ProtoMessage() {}
func (x *Policy_Timeout) ProtoReflect() protoreflect.Message { func (x *Policy_Timeout) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[4] mi := &file_app_policy_config_proto_msgTypes[4]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -301,14 +316,15 @@ type Policy_Stats struct {
UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink,proto3" json:"user_uplink,omitempty"` 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"` UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink,proto3" json:"user_downlink,omitempty"`
UserOnline bool `protobuf:"varint,3,opt,name=user_online,json=userOnline,proto3" json:"user_online,omitempty"`
} }
func (x *Policy_Stats) Reset() { func (x *Policy_Stats) Reset() {
*x = Policy_Stats{} *x = Policy_Stats{}
mi := &file_app_policy_config_proto_msgTypes[5] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[5]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Policy_Stats) String() string { func (x *Policy_Stats) String() string {
@@ -319,7 +335,7 @@ func (*Policy_Stats) ProtoMessage() {}
func (x *Policy_Stats) ProtoReflect() protoreflect.Message { func (x *Policy_Stats) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[5] mi := &file_app_policy_config_proto_msgTypes[5]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -348,13 +364,6 @@ func (x *Policy_Stats) GetUserDownlink() bool {
return false return false
} }
func (x *Policy_Stats) GetUserOnline() bool {
if x != nil {
return x.UserOnline
}
return false
}
type Policy_Buffer struct { type Policy_Buffer struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -366,9 +375,11 @@ type Policy_Buffer struct {
func (x *Policy_Buffer) Reset() { func (x *Policy_Buffer) Reset() {
*x = Policy_Buffer{} *x = Policy_Buffer{}
mi := &file_app_policy_config_proto_msgTypes[6] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[6]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *Policy_Buffer) String() string { func (x *Policy_Buffer) String() string {
@@ -379,7 +390,7 @@ func (*Policy_Buffer) ProtoMessage() {}
func (x *Policy_Buffer) ProtoReflect() protoreflect.Message { func (x *Policy_Buffer) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[6] mi := &file_app_policy_config_proto_msgTypes[6]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -414,9 +425,11 @@ type SystemPolicy_Stats struct {
func (x *SystemPolicy_Stats) Reset() { func (x *SystemPolicy_Stats) Reset() {
*x = SystemPolicy_Stats{} *x = SystemPolicy_Stats{}
mi := &file_app_policy_config_proto_msgTypes[7] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_policy_config_proto_msgTypes[7]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *SystemPolicy_Stats) String() string { func (x *SystemPolicy_Stats) String() string {
@@ -427,7 +440,7 @@ func (*SystemPolicy_Stats) ProtoMessage() {}
func (x *SystemPolicy_Stats) ProtoReflect() protoreflect.Message { func (x *SystemPolicy_Stats) ProtoReflect() protoreflect.Message {
mi := &file_app_policy_config_proto_msgTypes[7] mi := &file_app_policy_config_proto_msgTypes[7]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -477,7 +490,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, 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, 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, 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, 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, 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, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
@@ -504,51 +517,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, 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, 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, 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, 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, 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, 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, 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, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75, 0x66,
0x72, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x75, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
0x66, 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20,
0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, 0x69,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x1a,
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, 0xaf, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x62,
0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,
0x1a, 0xaf, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e,
0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f,
0x6b, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x6f,
0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03,
0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70,
0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e,
0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x6b, 0x22, 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, 0x05,
0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72,
0x6e, 0x6b, 0x22, 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43, 0x6f,
0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50,
0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51, 0x0a,
0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a,
0x0a, 0x0a, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75,
0x01, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xaa,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69,
0x63, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -564,7 +575,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_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 (*Second)(nil), // 0: xray.app.policy.Second
(*Policy)(nil), // 1: xray.app.policy.Policy (*Policy)(nil), // 1: xray.app.policy.Policy
(*SystemPolicy)(nil), // 2: xray.app.policy.SystemPolicy (*SystemPolicy)(nil), // 2: xray.app.policy.SystemPolicy
@@ -599,6 +610,104 @@ func file_app_policy_config_proto_init() {
if File_app_policy_config_proto != nil { if File_app_policy_config_proto != nil {
return 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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{

View File

@@ -22,7 +22,6 @@ message Policy {
message Stats { message Stats {
bool user_uplink = 1; bool user_uplink = 1;
bool user_downlink = 2; bool user_downlink = 2;
bool user_online = 3;
} }
message Buffer { message Buffer {

View File

@@ -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{})
}

View File

@@ -1,2 +1,4 @@
// Package policy is an implementation of policy.Manager feature. // Package policy is an implementation of policy.Manager feature.
package policy package policy
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen

View File

@@ -3,15 +3,13 @@ package command
import ( import (
"context" "context"
"github.com/xtls/xray-core/app/commander" grpc "google.golang.org/grpc"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/inbound" "github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/proxy"
grpc "google.golang.org/grpc"
) )
// InboundOperation is the interface for operations that applies to inbound handlers. // InboundOperation is the interface for operations that applies to inbound handlers.
@@ -29,7 +27,7 @@ type OutboundOperation interface {
func getInbound(handler inbound.Handler) (proxy.Inbound, error) { func getInbound(handler inbound.Handler) (proxy.Inbound, error) {
gi, ok := handler.(proxy.GetInbound) gi, ok := handler.(proxy.GetInbound)
if !ok { 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 return gi.GetInbound(), nil
} }
@@ -42,11 +40,11 @@ func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler inbound.Ha
} }
um, ok := p.(proxy.UserManager) um, ok := p.(proxy.UserManager)
if !ok { if !ok {
return errors.New("proxy is not a UserManager") return newError("proxy is not a UserManager")
} }
mUser, err := op.User.ToMemoryUser() mUser, err := op.User.ToMemoryUser()
if err != nil { 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) return um.AddUser(ctx, mUser)
} }
@@ -59,7 +57,7 @@ func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler inbound
} }
um, ok := p.(proxy.UserManager) um, ok := p.(proxy.UserManager)
if !ok { if !ok {
return errors.New("proxy is not a UserManager") return newError("proxy is not a UserManager")
} }
return um.RemoveUser(ctx, op.Email) return um.RemoveUser(ctx, op.Email)
} }
@@ -85,83 +83,21 @@ func (s *handlerServer) RemoveInbound(ctx context.Context, request *RemoveInboun
func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundRequest) (*AlterInboundResponse, error) { func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundRequest) (*AlterInboundResponse, error) {
rawOperation, err := request.Operation.GetInstance() rawOperation, err := request.Operation.GetInstance()
if err != nil { if err != nil {
return nil, errors.New("unknown operation").Base(err) return nil, newError("unknown operation").Base(err)
} }
operation, ok := rawOperation.(InboundOperation) operation, ok := rawOperation.(InboundOperation)
if !ok { 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) handler, err := s.ihm.GetHandler(ctx, request.Tag)
if err != nil { 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) 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) { func (s *handlerServer) AddOutbound(ctx context.Context, request *AddOutboundRequest) (*AddOutboundResponse, error) {
if err := core.AddOutboundHandler(s.s, request.Outbound); err != nil { if err := core.AddOutboundHandler(s.s, request.Outbound); err != nil {
return nil, err return nil, err
@@ -176,34 +112,17 @@ func (s *handlerServer) RemoveOutbound(ctx context.Context, request *RemoveOutbo
func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboundRequest) (*AlterOutboundResponse, error) { func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboundRequest) (*AlterOutboundResponse, error) {
rawOperation, err := request.Operation.GetInstance() rawOperation, err := request.Operation.GetInstance()
if err != nil { if err != nil {
return nil, errors.New("unknown operation").Base(err) return nil, newError("unknown operation").Base(err)
} }
operation, ok := rawOperation.(OutboundOperation) operation, ok := rawOperation.(OutboundOperation)
if !ok { if !ok {
return nil, errors.New("not an outbound operation") return nil, newError("not an outbound operation")
} }
handler := s.ohm.GetHandler(request.Tag) handler := s.ohm.GetHandler(request.Tag)
return &AlterOutboundResponse{}, operation.ApplyOutbound(ctx, handler) 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() {} func (s *handlerServer) mustEmbedUnimplementedHandlerServiceServer() {}
type service struct { type service struct {
@@ -217,7 +136,7 @@ func (s *service) Register(server *grpc.Server) {
common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) { common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) {
hs.ihm = im hs.ihm = im
hs.ohm = om hs.ohm = om
}, false)) }))
RegisterHandlerServiceServer(server, hs) RegisterHandlerServiceServer(server, hs)
// For compatibility purposes // For compatibility purposes

File diff suppressed because it is too large Load Diff

View File

@@ -37,27 +37,6 @@ message AlterInboundRequest {
message AlterInboundResponse {} 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 { message AddOutboundRequest {
core.OutboundHandlerConfig outbound = 1; core.OutboundHandlerConfig outbound = 1;
} }
@@ -77,12 +56,6 @@ message AlterOutboundRequest {
message AlterOutboundResponse {} message AlterOutboundResponse {}
message ListOutboundsRequest {}
message ListOutboundsResponse {
repeated core.OutboundHandlerConfig outbounds = 1;
}
service HandlerService { service HandlerService {
rpc AddInbound(AddInboundRequest) returns (AddInboundResponse) {} rpc AddInbound(AddInboundRequest) returns (AddInboundResponse) {}
@@ -90,19 +63,11 @@ service HandlerService {
rpc AlterInbound(AlterInboundRequest) returns (AlterInboundResponse) {} 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 AddOutbound(AddOutboundRequest) returns (AddOutboundResponse) {}
rpc RemoveOutbound(RemoveOutboundRequest) returns (RemoveOutboundResponse) {} rpc RemoveOutbound(RemoveOutboundRequest) returns (RemoveOutboundResponse) {}
rpc AlterOutbound(AlterOutboundRequest) returns (AlterOutboundResponse) {} rpc AlterOutbound(AlterOutboundRequest) returns (AlterOutboundResponse) {}
rpc ListOutbounds(ListOutboundsRequest) returns (ListOutboundsResponse) {}
} }
message Config {} message Config {}

View File

@@ -1,8 +1,4 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// source: app/proxyman/command/command.proto
package command package command
@@ -15,21 +11,8 @@ import (
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against. // is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later. // Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion9 const _ = grpc.SupportPackageIsVersion7
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"
)
// HandlerServiceClient is the client API for HandlerService service. // HandlerServiceClient is the client API for HandlerService service.
// //
@@ -38,13 +21,9 @@ type HandlerServiceClient interface {
AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error)
RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error)
AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, 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) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error)
RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error)
AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, 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 { type handlerServiceClient struct {
@@ -56,9 +35,8 @@ func NewHandlerServiceClient(cc grpc.ClientConnInterface) HandlerServiceClient {
} }
func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) { func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AddInboundResponse) 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 { if err != nil {
return nil, err return nil, err
} }
@@ -66,9 +44,8 @@ func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundReq
} }
func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) { func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveInboundResponse) 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 { if err != nil {
return nil, err return nil, err
} }
@@ -76,39 +53,8 @@ func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInbo
} }
func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) { func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AlterInboundResponse) out := new(AlterInboundResponse)
err := c.cc.Invoke(ctx, HandlerService_AlterInbound_FullMethodName, in, out, cOpts...) err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AlterInbound", in, out, opts...)
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...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -116,9 +62,8 @@ func (c *handlerServiceClient) GetInboundUsersCount(ctx context.Context, in *Get
} }
func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) { func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AddOutboundResponse) 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 { if err != nil {
return nil, err return nil, err
} }
@@ -126,9 +71,8 @@ func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundR
} }
func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) { func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveOutboundResponse) 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 { if err != nil {
return nil, err return nil, err
} }
@@ -136,19 +80,8 @@ func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOut
} }
func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) { func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AlterOutboundResponse) out := new(AlterOutboundResponse)
err := c.cc.Invoke(ctx, HandlerService_AlterOutbound_FullMethodName, in, out, cOpts...) err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AlterOutbound", in, out, opts...)
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...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -157,27 +90,20 @@ func (c *handlerServiceClient) ListOutbounds(ctx context.Context, in *ListOutbou
// HandlerServiceServer is the server API for HandlerService service. // HandlerServiceServer is the server API for HandlerService service.
// All implementations must embed UnimplementedHandlerServiceServer // All implementations must embed UnimplementedHandlerServiceServer
// for forward compatibility. // for forward compatibility
type HandlerServiceServer interface { type HandlerServiceServer interface {
AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error)
RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error)
AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, 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) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error)
RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error)
AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error)
ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error)
mustEmbedUnimplementedHandlerServiceServer() mustEmbedUnimplementedHandlerServiceServer()
} }
// UnimplementedHandlerServiceServer must be embedded to have // UnimplementedHandlerServiceServer must be embedded to have forward compatible implementations.
// forward compatible implementations. type UnimplementedHandlerServiceServer struct {
// }
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedHandlerServiceServer struct{}
func (UnimplementedHandlerServiceServer) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) { func (UnimplementedHandlerServiceServer) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddInbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method AddInbound not implemented")
@@ -188,15 +114,6 @@ func (UnimplementedHandlerServiceServer) RemoveInbound(context.Context, *RemoveI
func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) { func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AlterInbound not implemented") 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) { func (UnimplementedHandlerServiceServer) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddOutbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method AddOutbound not implemented")
} }
@@ -206,11 +123,7 @@ func (UnimplementedHandlerServiceServer) RemoveOutbound(context.Context, *Remove
func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) { func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AlterOutbound not implemented") 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) mustEmbedUnimplementedHandlerServiceServer() {}
func (UnimplementedHandlerServiceServer) testEmbeddedByValue() {}
// UnsafeHandlerServiceServer may be embedded to opt out of forward compatibility for this service. // 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 // Use of this interface is not recommended, as added methods to HandlerServiceServer will
@@ -220,13 +133,6 @@ type UnsafeHandlerServiceServer interface {
} }
func RegisterHandlerServiceServer(s grpc.ServiceRegistrar, srv HandlerServiceServer) { 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) s.RegisterService(&HandlerService_ServiceDesc, srv)
} }
@@ -240,7 +146,7 @@ func _HandlerService_AddInbound_Handler(srv interface{}, ctx context.Context, de
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: HandlerService_AddInbound_FullMethodName, FullMethod: "/xray.app.proxyman.command.HandlerService/AddInbound",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest)) return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest))
@@ -258,7 +164,7 @@ func _HandlerService_RemoveInbound_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: HandlerService_RemoveInbound_FullMethodName, FullMethod: "/xray.app.proxyman.command.HandlerService/RemoveInbound",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest)) return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest))
@@ -276,7 +182,7 @@ func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: HandlerService_AlterInbound_FullMethodName, FullMethod: "/xray.app.proxyman.command.HandlerService/AlterInbound",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest)) return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest))
@@ -284,60 +190,6 @@ func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler) 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) { func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddOutboundRequest) in := new(AddOutboundRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@@ -348,7 +200,7 @@ func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, d
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: HandlerService_AddOutbound_FullMethodName, FullMethod: "/xray.app.proxyman.command.HandlerService/AddOutbound",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest)) return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest))
@@ -366,7 +218,7 @@ func _HandlerService_RemoveOutbound_Handler(srv interface{}, ctx context.Context
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: HandlerService_RemoveOutbound_FullMethodName, FullMethod: "/xray.app.proxyman.command.HandlerService/RemoveOutbound",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest)) return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest))
@@ -384,7 +236,7 @@ func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: HandlerService_AlterOutbound_FullMethodName, FullMethod: "/xray.app.proxyman.command.HandlerService/AlterOutbound",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest)) return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest))
@@ -392,24 +244,6 @@ func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler) 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. // HandlerService_ServiceDesc is the grpc.ServiceDesc for HandlerService service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@@ -429,18 +263,6 @@ var HandlerService_ServiceDesc = grpc.ServiceDesc{
MethodName: "AlterInbound", MethodName: "AlterInbound",
Handler: _HandlerService_AlterInbound_Handler, Handler: _HandlerService_AlterInbound_Handler,
}, },
{
MethodName: "ListInbounds",
Handler: _HandlerService_ListInbounds_Handler,
},
{
MethodName: "GetInboundUsers",
Handler: _HandlerService_GetInboundUsers_Handler,
},
{
MethodName: "GetInboundUsersCount",
Handler: _HandlerService_GetInboundUsersCount_Handler,
},
{ {
MethodName: "AddOutbound", MethodName: "AddOutbound",
Handler: _HandlerService_AddOutbound_Handler, Handler: _HandlerService_AddOutbound_Handler,
@@ -453,10 +275,6 @@ var HandlerService_ServiceDesc = grpc.ServiceDesc{
MethodName: "AlterOutbound", MethodName: "AlterOutbound",
Handler: _HandlerService_AlterOutbound_Handler, Handler: _HandlerService_AlterOutbound_Handler,
}, },
{
MethodName: "ListOutbounds",
Handler: _HandlerService_ListOutbounds_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "app/proxyman/command/command.proto", Metadata: "app/proxyman/command/command.proto",

View File

@@ -1 +1,3 @@
package command package command
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen

View File

@@ -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{})
}

View File

@@ -1,5 +1,12 @@
package proxyman package proxyman
import (
"github.com/xtls/xray-core/common/matcher/domain"
"github.com/xtls/xray-core/common/matcher/geoip"
)
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
func (s *AllocationStrategy) GetConcurrencyValue() uint32 { func (s *AllocationStrategy) GetConcurrencyValue() uint32 {
if s == nil || s.Concurrency == nil { if s == nil || s.Concurrency == nil {
return 3 return 3
@@ -19,5 +26,50 @@ func (c *ReceiverConfig) GetEffectiveSniffingSettings() *SniffingConfig {
return c.SniffingSettings 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 return nil
} }
type SniffingMatcher struct {
ExDomain *domain.DomainMatcher
ExIP *geoip.MultiGeoIPMatcher
}
func NewSniffingMatcher(sc *SniffingConfig) (*SniffingMatcher, error) {
m := new(SniffingMatcher)
if sc == nil {
return m, nil
}
if sc.DomainsExcluded != nil {
exDomain, err := domain.NewDomainMatcher(sc.DomainsExcluded)
if err != nil {
return nil, newError("failed to parse domain").Base(err)
}
m.ExDomain = exDomain
}
if sc.IpsExcluded != nil {
exIP, err := geoip.NewMultiGeoIPMatcher(sc.IpsExcluded, true)
if err != nil {
return nil, newError("failed to parse ip").Base(err)
}
m.ExIP = exIP
}
return m, nil
}

View File

@@ -1,12 +1,15 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.25.0
// protoc v5.28.2 // protoc v3.15.6
// source: app/proxyman/config.proto // source: app/proxyman/config.proto
package proxyman package proxyman
import ( import (
proto "github.com/golang/protobuf/proto"
domain "github.com/xtls/xray-core/common/matcher/domain"
geoip "github.com/xtls/xray-core/common/matcher/geoip"
net "github.com/xtls/xray-core/common/net" net "github.com/xtls/xray-core/common/net"
serial "github.com/xtls/xray-core/common/serial" serial "github.com/xtls/xray-core/common/serial"
internet "github.com/xtls/xray-core/transport/internet" internet "github.com/xtls/xray-core/transport/internet"
@@ -23,6 +26,56 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
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 type AllocationStrategy_Type int32
const ( const (
@@ -59,11 +112,11 @@ func (x AllocationStrategy_Type) String() string {
} }
func (AllocationStrategy_Type) Descriptor() protoreflect.EnumDescriptor { 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 { 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 { func (x AllocationStrategy_Type) Number() protoreflect.EnumNumber {
@@ -83,9 +136,11 @@ type InboundConfig struct {
func (x *InboundConfig) Reset() { func (x *InboundConfig) Reset() {
*x = InboundConfig{} *x = InboundConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[0] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[0]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *InboundConfig) String() string { func (x *InboundConfig) String() string {
@@ -96,7 +151,7 @@ func (*InboundConfig) ProtoMessage() {}
func (x *InboundConfig) ProtoReflect() protoreflect.Message { func (x *InboundConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[0] mi := &file_app_proxyman_config_proto_msgTypes[0]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -127,9 +182,11 @@ type AllocationStrategy struct {
func (x *AllocationStrategy) Reset() { func (x *AllocationStrategy) Reset() {
*x = AllocationStrategy{} *x = AllocationStrategy{}
mi := &file_app_proxyman_config_proto_msgTypes[1] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[1]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *AllocationStrategy) String() string { func (x *AllocationStrategy) String() string {
@@ -140,7 +197,7 @@ func (*AllocationStrategy) ProtoMessage() {}
func (x *AllocationStrategy) ProtoReflect() protoreflect.Message { func (x *AllocationStrategy) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[1] mi := &file_app_proxyman_config_proto_msgTypes[1]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -185,20 +242,21 @@ type SniffingConfig struct {
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// Override target destination if sniff'ed protocol is in the given list. // Override target destination if sniff'ed protocol is in the given list.
// Supported values are "http", "tls", "fakedns". // Supported values are "http", "tls", "fakedns".
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
DomainsExcluded []string `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"` DomainsExcluded []*domain.Domain `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
IpsExcluded []*geoip.GeoIP `protobuf:"bytes,5,rep,name=ips_excluded,json=ipsExcluded,proto3" json:"ips_excluded,omitempty"`
// Whether should only try to sniff metadata without waiting for client input. // Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first // Can be used to support SMTP like protocol where server send the first message.
// message.
MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"` MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"`
RouteOnly bool `protobuf:"varint,5,opt,name=route_only,json=routeOnly,proto3" json:"route_only,omitempty"`
} }
func (x *SniffingConfig) Reset() { func (x *SniffingConfig) Reset() {
*x = SniffingConfig{} *x = SniffingConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[2] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[2]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *SniffingConfig) String() string { func (x *SniffingConfig) String() string {
@@ -209,7 +267,7 @@ func (*SniffingConfig) ProtoMessage() {}
func (x *SniffingConfig) ProtoReflect() protoreflect.Message { func (x *SniffingConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[2] mi := &file_app_proxyman_config_proto_msgTypes[2]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -238,13 +296,20 @@ func (x *SniffingConfig) GetDestinationOverride() []string {
return nil return nil
} }
func (x *SniffingConfig) GetDomainsExcluded() []string { func (x *SniffingConfig) GetDomainsExcluded() []*domain.Domain {
if x != nil { if x != nil {
return x.DomainsExcluded return x.DomainsExcluded
} }
return nil return nil
} }
func (x *SniffingConfig) GetIpsExcluded() []*geoip.GeoIP {
if x != nil {
return x.IpsExcluded
}
return nil
}
func (x *SniffingConfig) GetMetadataOnly() bool { func (x *SniffingConfig) GetMetadataOnly() bool {
if x != nil { if x != nil {
return x.MetadataOnly return x.MetadataOnly
@@ -252,33 +317,33 @@ func (x *SniffingConfig) GetMetadataOnly() bool {
return false return false
} }
func (x *SniffingConfig) GetRouteOnly() bool {
if x != nil {
return x.RouteOnly
}
return false
}
type ReceiverConfig struct { type ReceiverConfig struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// PortList specifies the ports which the Receiver should listen on. // PortRange specifies the ports which the Receiver should listen on.
PortList *net.PortList `protobuf:"bytes,1,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"` PortRange *net.PortRange `protobuf:"bytes,1,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"`
// Listen specifies the IP address that the Receiver should listen on. // Listen specifies the IP address that the Receiver should listen on.
Listen *net.IPOrDomain `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"` Listen *net.IPOrDomain `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"`
AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy,proto3" json:"allocation_strategy,omitempty"` 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"` 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"` 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() { func (x *ReceiverConfig) Reset() {
*x = ReceiverConfig{} *x = ReceiverConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[3] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[3]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *ReceiverConfig) String() string { func (x *ReceiverConfig) String() string {
@@ -289,7 +354,7 @@ func (*ReceiverConfig) ProtoMessage() {}
func (x *ReceiverConfig) ProtoReflect() protoreflect.Message { func (x *ReceiverConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[3] mi := &file_app_proxyman_config_proto_msgTypes[3]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -304,9 +369,9 @@ func (*ReceiverConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{3} return file_app_proxyman_config_proto_rawDescGZIP(), []int{3}
} }
func (x *ReceiverConfig) GetPortList() *net.PortList { func (x *ReceiverConfig) GetPortRange() *net.PortRange {
if x != nil { if x != nil {
return x.PortList return x.PortRange
} }
return nil return nil
} }
@@ -339,6 +404,14 @@ func (x *ReceiverConfig) GetReceiveOriginalDestination() bool {
return false return false
} }
// Deprecated: Do not use.
func (x *ReceiverConfig) GetDomainOverride() []KnownProtocols {
if x != nil {
return x.DomainOverride
}
return nil
}
func (x *ReceiverConfig) GetSniffingSettings() *SniffingConfig { func (x *ReceiverConfig) GetSniffingSettings() *SniffingConfig {
if x != nil { if x != nil {
return x.SniffingSettings return x.SniffingSettings
@@ -358,9 +431,11 @@ type InboundHandlerConfig struct {
func (x *InboundHandlerConfig) Reset() { func (x *InboundHandlerConfig) Reset() {
*x = InboundHandlerConfig{} *x = InboundHandlerConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[4] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[4]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *InboundHandlerConfig) String() string { func (x *InboundHandlerConfig) String() string {
@@ -371,7 +446,7 @@ func (*InboundHandlerConfig) ProtoMessage() {}
func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message { func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[4] mi := &file_app_proxyman_config_proto_msgTypes[4]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -415,9 +490,11 @@ type OutboundConfig struct {
func (x *OutboundConfig) Reset() { func (x *OutboundConfig) Reset() {
*x = OutboundConfig{} *x = OutboundConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[5] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[5]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *OutboundConfig) String() string { func (x *OutboundConfig) String() string {
@@ -428,7 +505,7 @@ func (*OutboundConfig) ProtoMessage() {}
func (x *OutboundConfig) ProtoReflect() protoreflect.Message { func (x *OutboundConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[5] mi := &file_app_proxyman_config_proto_msgTypes[5]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -453,14 +530,15 @@ type SenderConfig struct {
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_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"` 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() { func (x *SenderConfig) Reset() {
*x = SenderConfig{} *x = SenderConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[6] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[6]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *SenderConfig) String() string { func (x *SenderConfig) String() string {
@@ -471,7 +549,7 @@ func (*SenderConfig) ProtoMessage() {}
func (x *SenderConfig) ProtoReflect() protoreflect.Message { func (x *SenderConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[6] mi := &file_app_proxyman_config_proto_msgTypes[6]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -514,13 +592,6 @@ func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig {
return nil return nil
} }
func (x *SenderConfig) GetViaCidr() string {
if x != nil {
return x.ViaCidr
}
return ""
}
type MultiplexingConfig struct { type MultiplexingConfig struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -529,18 +600,16 @@ type MultiplexingConfig struct {
// Whether or not Mux is enabled. // Whether or not Mux is enabled.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// Max number of concurrent connections that one Mux connection can handle. // Max number of concurrent connections that one Mux connection can handle.
Concurrency int32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"` Concurrency uint32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
// Transport XUDP in another Mux.
XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"`
// "reject" (default), "allow" or "skip".
XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"`
} }
func (x *MultiplexingConfig) Reset() { func (x *MultiplexingConfig) Reset() {
*x = MultiplexingConfig{} *x = MultiplexingConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[7] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[7]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *MultiplexingConfig) String() string { func (x *MultiplexingConfig) String() string {
@@ -551,7 +620,7 @@ func (*MultiplexingConfig) ProtoMessage() {}
func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message { func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[7] mi := &file_app_proxyman_config_proto_msgTypes[7]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -573,27 +642,13 @@ func (x *MultiplexingConfig) GetEnabled() bool {
return false return false
} }
func (x *MultiplexingConfig) GetConcurrency() int32 { func (x *MultiplexingConfig) GetConcurrency() uint32 {
if x != nil { if x != nil {
return x.Concurrency return x.Concurrency
} }
return 0 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 { type AllocationStrategy_AllocationStrategyConcurrency struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -604,9 +659,11 @@ type AllocationStrategy_AllocationStrategyConcurrency struct {
func (x *AllocationStrategy_AllocationStrategyConcurrency) Reset() { func (x *AllocationStrategy_AllocationStrategyConcurrency) Reset() {
*x = AllocationStrategy_AllocationStrategyConcurrency{} *x = AllocationStrategy_AllocationStrategyConcurrency{}
mi := &file_app_proxyman_config_proto_msgTypes[8] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[8]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *AllocationStrategy_AllocationStrategyConcurrency) String() string { func (x *AllocationStrategy_AllocationStrategyConcurrency) String() string {
@@ -617,7 +674,7 @@ func (*AllocationStrategy_AllocationStrategyConcurrency) ProtoMessage() {}
func (x *AllocationStrategy_AllocationStrategyConcurrency) ProtoReflect() protoreflect.Message { func (x *AllocationStrategy_AllocationStrategyConcurrency) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[8] mi := &file_app_proxyman_config_proto_msgTypes[8]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -649,9 +706,11 @@ type AllocationStrategy_AllocationStrategyRefresh struct {
func (x *AllocationStrategy_AllocationStrategyRefresh) Reset() { func (x *AllocationStrategy_AllocationStrategyRefresh) Reset() {
*x = AllocationStrategy_AllocationStrategyRefresh{} *x = AllocationStrategy_AllocationStrategyRefresh{}
mi := &file_app_proxyman_config_proto_msgTypes[9] if protoimpl.UnsafeEnabled {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) mi := &file_app_proxyman_config_proto_msgTypes[9]
ms.StoreMessageInfo(mi) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
} }
func (x *AllocationStrategy_AllocationStrategyRefresh) String() string { func (x *AllocationStrategy_AllocationStrategyRefresh) String() string {
@@ -662,7 +721,7 @@ func (*AllocationStrategy_AllocationStrategyRefresh) ProtoMessage() {}
func (x *AllocationStrategy_AllocationStrategyRefresh) ProtoReflect() protoreflect.Message { func (x *AllocationStrategy_AllocationStrategyRefresh) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[9] mi := &file_app_proxyman_config_proto_msgTypes[9]
if x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -689,134 +748,144 @@ var File_app_proxyman_config_proto protoreflect.FileDescriptor
var file_app_proxyman_config_proto_rawDesc = []byte{ var file_app_proxyman_config_proto_rawDesc = []byte{
0x0a, 0x19, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f, 0x63, 0x0a, 0x19, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x78, 0x72, 0x61, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x1a, 0x18, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x1a, 0x22,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x64,
0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x74, 0x6f, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x63, 0x68,
0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2e, 0x70,
0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74,
0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15,
0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x6f, 0x74, 0x6f, 0x22, 0x0f, 0x0a, 0x0d, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x6e, 0x66, 0x69, 0x67, 0x22, 0xae, 0x03, 0x0a, 0x12, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73,
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 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, 0x0f, 0x0a, 0x0d, 0x49, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xae, 0x03, 0x0a, 0x12, 0x41,
0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
0x79, 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70,
0x65, 0x12, 0x65, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x41, 0x6c,
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e,
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x59, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72,
0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c,
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x65, 0x0a, 0x0b, 0x63, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x65, 0x67, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72,
0x32, 0x43, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x65, 0x73, 0x68, 0x1a, 0x35, 0x0a, 0x1d, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20,
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x31, 0x0a, 0x19, 0x41, 0x6c,
0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x63, 0x79, 0x12, 0x59, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a,
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10,
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x66, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0x96, 0x02, 0x0a, 0x0e,
0x72, 0x65, 0x73, 0x68, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x1a, 0x35, 0x0a, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
0x1d, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x65, 0x67, 0x79, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
0x61, 0x6c, 0x75, 0x65, 0x1a, 0x31, 0x0a, 0x19, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x64,
0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x64, 0x6f, 0x6d, 0x61,
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0xcc, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0c, 0x69, 0x70,
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x73, 0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d,
0x65, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2e, 0x47, 0x65, 0x6f,
0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x49, 0x50, 0x52, 0x0b, 0x69, 0x70, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x12,
0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x6c, 0x79,
0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x90, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f,
0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x6c, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72,
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f,
0x61, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e,
0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01,
0x4f, 0x6e, 0x6c, 0x79, 0x22, 0xbd, 0x03, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63,
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03,
0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x33, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c,
0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x6c, 0x69, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12,
0x73, 0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4e, 0x0a, 0x0f, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69,
0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72,
0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x4e, 0x0a, 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, 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, 0x08, 0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74,
0x61, 0x67, 0x12, 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69,
0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52,
0x10, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54,
0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75,
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 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, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a, 0x0f,
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0e,
0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03,
0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x50,
0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78,
0x0a, 0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75, 0x6c,
0x6e, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x6e, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70,
0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4a, 0x04, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75,
0x08, 0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22,
0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x50, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43,
0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02,
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63,
0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x72, 0x65, 0x79, 0x2a, 0x23, 0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x6f, 0x6c, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a,
0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50,
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79,
0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xcb, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70,
0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72,
0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72,
0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
0x6c, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78,
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69,
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08,
0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74,
0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75,
0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x78,
0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x42, 0x55,
0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f,
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -831,46 +900,52 @@ func file_app_proxyman_config_proto_rawDescGZIP() []byte {
return file_app_proxyman_config_proto_rawDescData 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_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_app_proxyman_config_proto_goTypes = []any{ var file_app_proxyman_config_proto_goTypes = []interface{}{
(AllocationStrategy_Type)(0), // 0: xray.app.proxyman.AllocationStrategy.Type (KnownProtocols)(0), // 0: xray.app.proxyman.KnownProtocols
(*InboundConfig)(nil), // 1: xray.app.proxyman.InboundConfig (AllocationStrategy_Type)(0), // 1: xray.app.proxyman.AllocationStrategy.Type
(*AllocationStrategy)(nil), // 2: xray.app.proxyman.AllocationStrategy (*InboundConfig)(nil), // 2: xray.app.proxyman.InboundConfig
(*SniffingConfig)(nil), // 3: xray.app.proxyman.SniffingConfig (*AllocationStrategy)(nil), // 3: xray.app.proxyman.AllocationStrategy
(*ReceiverConfig)(nil), // 4: xray.app.proxyman.ReceiverConfig (*SniffingConfig)(nil), // 4: xray.app.proxyman.SniffingConfig
(*InboundHandlerConfig)(nil), // 5: xray.app.proxyman.InboundHandlerConfig (*ReceiverConfig)(nil), // 5: xray.app.proxyman.ReceiverConfig
(*OutboundConfig)(nil), // 6: xray.app.proxyman.OutboundConfig (*InboundHandlerConfig)(nil), // 6: xray.app.proxyman.InboundHandlerConfig
(*SenderConfig)(nil), // 7: xray.app.proxyman.SenderConfig (*OutboundConfig)(nil), // 7: xray.app.proxyman.OutboundConfig
(*MultiplexingConfig)(nil), // 8: xray.app.proxyman.MultiplexingConfig (*SenderConfig)(nil), // 8: xray.app.proxyman.SenderConfig
(*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 9: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency (*MultiplexingConfig)(nil), // 9: xray.app.proxyman.MultiplexingConfig
(*AllocationStrategy_AllocationStrategyRefresh)(nil), // 10: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh (*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 10: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
(*net.PortList)(nil), // 11: xray.common.net.PortList (*AllocationStrategy_AllocationStrategyRefresh)(nil), // 11: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
(*net.IPOrDomain)(nil), // 12: xray.common.net.IPOrDomain (*domain.Domain)(nil), // 12: xray.common.matcher.domain.Domain
(*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig (*geoip.GeoIP)(nil), // 13: xray.common.matcher.geoip.GeoIP
(*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage (*net.PortRange)(nil), // 14: xray.common.net.PortRange
(*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig (*net.IPOrDomain)(nil), // 15: xray.common.net.IPOrDomain
(*internet.StreamConfig)(nil), // 16: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 18: xray.transport.internet.ProxyConfig
} }
var file_app_proxyman_config_proto_depIdxs = []int32{ var file_app_proxyman_config_proto_depIdxs = []int32{
0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type 1, // 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, // 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, // 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, // 3: xray.app.proxyman.SniffingConfig.domains_excluded:type_name -> xray.common.matcher.domain.Domain
12, // 4: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain 13, // 4: xray.app.proxyman.SniffingConfig.ips_excluded:type_name -> xray.common.matcher.geoip.GeoIP
2, // 5: xray.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> xray.app.proxyman.AllocationStrategy 14, // 5: xray.app.proxyman.ReceiverConfig.port_range:type_name -> xray.common.net.PortRange
13, // 6: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 15, // 6: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
3, // 7: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig 3, // 7: xray.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> xray.app.proxyman.AllocationStrategy
14, // 8: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage 16, // 8: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
14, // 9: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage 0, // 9: xray.app.proxyman.ReceiverConfig.domain_override:type_name -> xray.app.proxyman.KnownProtocols
12, // 10: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain 4, // 10: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 17, // 11: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig 17, // 12: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig 15, // 13: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
14, // [14:14] is the sub-list for method output_type 16, // 14: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
14, // [14:14] is the sub-list for method input_type 18, // 15: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
14, // [14:14] is the sub-list for extension type_name 9, // 16: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
14, // [14:14] is the sub-list for extension extendee 17, // [17:17] is the sub-list for method output_type
0, // [0:14] is the sub-list for field type_name 17, // [17:17] is the sub-list for method input_type
17, // [17:17] is the sub-list for extension type_name
17, // [17:17] is the sub-list for extension extendee
0, // [0:17] is the sub-list for field type_name
} }
func init() { file_app_proxyman_config_proto_init() } func init() { file_app_proxyman_config_proto_init() }
@@ -878,12 +953,134 @@ func file_app_proxyman_config_proto_init() {
if File_app_proxyman_config_proto != nil { if File_app_proxyman_config_proto != nil {
return 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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_proxyman_config_proto_rawDesc, RawDescriptor: file_app_proxyman_config_proto_rawDesc,
NumEnums: 1, NumEnums: 2,
NumMessages: 10, NumMessages: 10,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,

View File

@@ -6,6 +6,8 @@ option go_package = "github.com/xtls/xray-core/app/proxyman";
option java_package = "com.xray.app.proxyman"; option java_package = "com.xray.app.proxyman";
option java_multiple_files = true; option java_multiple_files = true;
import "common/matcher/domain/domain.proto";
import "common/matcher/geoip/geoip.proto";
import "common/net/address.proto"; import "common/net/address.proto";
import "common/net/port.proto"; import "common/net/port.proto";
import "transport/internet/config.proto"; import "transport/internet/config.proto";
@@ -27,19 +29,28 @@ message AllocationStrategy {
Type type = 1; Type type = 1;
message AllocationStrategyConcurrency { uint32 value = 1; } message AllocationStrategyConcurrency {
uint32 value = 1;
}
// Number of handlers (ports) running in parallel. // Number of handlers (ports) running in parallel.
// Default value is 3 if unset. // Default value is 3 if unset.
AllocationStrategyConcurrency concurrency = 2; AllocationStrategyConcurrency concurrency = 2;
message AllocationStrategyRefresh { uint32 value = 1; } message AllocationStrategyRefresh {
uint32 value = 1;
}
// Number of minutes before a handler is regenerated. // Number of minutes before a handler is regenerated.
// Default value is 5 if unset. // Default value is 5 if unset.
AllocationStrategyRefresh refresh = 3; AllocationStrategyRefresh refresh = 3;
} }
enum KnownProtocols {
HTTP = 0;
TLS = 1;
}
message SniffingConfig { message SniffingConfig {
// Whether or not to enable content sniffing on an inbound connection. // Whether or not to enable content sniffing on an inbound connection.
bool enabled = 1; bool enabled = 1;
@@ -47,26 +58,28 @@ message SniffingConfig {
// Override target destination if sniff'ed protocol is in the given list. // Override target destination if sniff'ed protocol is in the given list.
// Supported values are "http", "tls", "fakedns". // Supported values are "http", "tls", "fakedns".
repeated string destination_override = 2; repeated string destination_override = 2;
repeated string domains_excluded = 3;
repeated xray.common.matcher.domain.Domain domains_excluded = 3;
repeated xray.common.matcher.geoip.GeoIP ips_excluded = 5;
// Whether should only try to sniff metadata without waiting for client input. // Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first // Can be used to support SMTP like protocol where server send the first message.
// message.
bool metadata_only = 4; bool metadata_only = 4;
bool route_only = 5;
} }
message ReceiverConfig { message ReceiverConfig {
// PortList specifies the ports which the Receiver should listen on. // PortRange specifies the ports which the Receiver should listen on.
xray.common.net.PortList port_list = 1; xray.common.net.PortRange port_range = 1;
// Listen specifies the IP address that the Receiver should listen on. // Listen specifies the IP address that the Receiver should listen on.
xray.common.net.IPOrDomain listen = 2; xray.common.net.IPOrDomain listen = 2;
AllocationStrategy allocation_strategy = 3; AllocationStrategy allocation_strategy = 3;
xray.transport.internet.StreamConfig stream_settings = 4; xray.transport.internet.StreamConfig stream_settings = 4;
bool receive_original_destination = 5; bool receive_original_destination = 5;
reserved 6; 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 { message InboundHandlerConfig {
@@ -83,16 +96,11 @@ message SenderConfig {
xray.transport.internet.StreamConfig stream_settings = 2; xray.transport.internet.StreamConfig stream_settings = 2;
xray.transport.internet.ProxyConfig proxy_settings = 3; xray.transport.internet.ProxyConfig proxy_settings = 3;
MultiplexingConfig multiplex_settings = 4; MultiplexingConfig multiplex_settings = 4;
string via_cidr = 5;
} }
message MultiplexingConfig { message MultiplexingConfig {
// Whether or not Mux is enabled. // Whether or not Mux is enabled.
bool enabled = 1; bool enabled = 1;
// Max number of concurrent connections that one Mux connection can handle. // Max number of concurrent connections that one Mux connection can handle.
int32 concurrency = 2; uint32 concurrency = 2;
// Transport XUDP in another Mux.
int32 xudpConcurrency = 3;
// "reject" (default), "allow" or "skip".
string xudpProxyUDP443 = 4;
} }

View File

@@ -0,0 +1,9 @@
package proxyman
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

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