mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 01:56:48 +08:00
Compare commits
53 Commits
v25.1.30
...
v1.250306.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2cba2c4d59 | ||
![]() |
306fa51475 | ||
![]() |
6d6f1c6967 | ||
![]() |
152959824f | ||
![]() |
a977b6357e | ||
![]() |
16eee1b89c | ||
![]() |
dde0a4f272 | ||
![]() |
e15dff94b5 | ||
![]() |
e466b0497c | ||
![]() |
b9cb93d3c2 | ||
![]() |
8d46f7e14c | ||
![]() |
4b616f5cd0 | ||
![]() |
06b4a7ce4d | ||
![]() |
4c12e1686b | ||
![]() |
225d151cd3 | ||
![]() |
d451078e72 | ||
![]() |
ce2384cccc | ||
![]() |
be43f66b63 | ||
![]() |
71a6d89c23 | ||
![]() |
89792aee9d | ||
![]() |
b786a50aee | ||
![]() |
b38a53e629 | ||
![]() |
52381a3c03 | ||
![]() |
4b01eb4398 | ||
![]() |
c5de08bea6 | ||
![]() |
8cb63db6c0 | ||
![]() |
eef74b2c7d | ||
![]() |
a1714cc4ce | ||
![]() |
958b13ebb5 | ||
![]() |
22c50a70c6 | ||
![]() |
94c7970fd6 | ||
![]() |
a71762b5da | ||
![]() |
5033cbceea | ||
![]() |
dcd7e92c45 | ||
![]() |
2d7ca4a6a6 | ||
![]() |
925a985cc0 | ||
![]() |
613c63b165 | ||
![]() |
d4c7cd02fd | ||
![]() |
db5f18b98c | ||
![]() |
c81d8e488a | ||
![]() |
1d9e6bc2f3 | ||
![]() |
ae327eb7e6 | ||
![]() |
e893fa1828 | ||
![]() |
1982c2366e | ||
![]() |
117de1fd3c | ||
![]() |
07c35ed52a | ||
![]() |
e17c068821 | ||
![]() |
88d40d6367 | ||
![]() |
527caa3711 | ||
![]() |
c6a31f457c | ||
![]() |
9b7841178a | ||
![]() |
480c7d7db7 | ||
![]() |
c2f6c89987 |
27
.github/workflows/release-win7.yml
vendored
27
.github/workflows/release-win7.yml
vendored
@@ -1,11 +1,5 @@
|
|||||||
name: Build and Release for Windows 7
|
name: Build and Release for Windows 7
|
||||||
|
|
||||||
# NOTE: This Github Actions file depends on the Makefile.
|
|
||||||
# Building the correct package requires the correct binaries generated by the Makefile. To
|
|
||||||
# ensure the correct output, the Makefile must accept the appropriate input and compile the
|
|
||||||
# correct file with the correct name. If you need to modify this file, please ensure it won't
|
|
||||||
# disrupt the Makefile.
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
@@ -37,6 +31,9 @@ jobs:
|
|||||||
GOARCH: ${{ matrix.goarch }}
|
GOARCH: ${{ matrix.goarch }}
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
steps:
|
steps:
|
||||||
|
- name: Checkout codebase
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Show workflow information
|
- name: Show workflow information
|
||||||
run: |
|
run: |
|
||||||
_NAME=${{ matrix.assetname }}
|
_NAME=${{ matrix.assetname }}
|
||||||
@@ -46,18 +43,17 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: stable
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Setup patched builder
|
- name: Setup patched builder
|
||||||
run: |
|
run: |
|
||||||
GOSDK=$(go env GOROOT)
|
GOSDK=$(go env GOROOT)
|
||||||
curl -O -L https://github.com/XTLS/go-win7/releases/latest/download/go-for-win7-linux-amd64.zip
|
|
||||||
rm -r $GOSDK/*
|
rm -r $GOSDK/*
|
||||||
|
cd $GOSDK
|
||||||
|
curl -O -L https://github.com/XTLS/go-win7/releases/latest/download/go-for-win7-linux-amd64.zip
|
||||||
unzip ./go-for-win7-linux-amd64.zip -d $GOSDK
|
unzip ./go-for-win7-linux-amd64.zip -d $GOSDK
|
||||||
|
rm ./go-for-win7-linux-amd64.zip
|
||||||
- name: Checkout codebase
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get project dependencies
|
- name: Get project dependencies
|
||||||
run: go mod download
|
run: go mod download
|
||||||
@@ -65,8 +61,13 @@ jobs:
|
|||||||
- name: Build Xray
|
- name: Build Xray
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build_assets
|
mkdir -p build_assets
|
||||||
make
|
COMMID=$(git describe --always --dirty)
|
||||||
find . -maxdepth 1 -type f -regex './\(wxray\|xray\).exe' -exec mv {} ./build_assets/ \;
|
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
|
- name: Restore Geodat Cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
|
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@@ -1,11 +1,5 @@
|
|||||||
name: Build and Release
|
name: Build and Release
|
||||||
|
|
||||||
# NOTE: This Github Actions file depends on the Makefile.
|
|
||||||
# Building the correct package requires the correct binaries generated by the Makefile. To
|
|
||||||
# ensure the correct output, the Makefile must accept the appropriate input and compile the
|
|
||||||
# correct file with the correct name. If you need to modify this file, please ensure it won't
|
|
||||||
# disrupt the Makefile.
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
@@ -129,8 +123,22 @@ jobs:
|
|||||||
- name: Build Xray
|
- name: Build Xray
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build_assets
|
mkdir -p build_assets
|
||||||
make
|
COMMID=$(git describe --always --dirty)
|
||||||
find . -maxdepth 1 -type f -regex './\(wxray\|xray\|xray_softfloat\)\(\|.exe\)' -exec mv {} ./build_assets/ \;
|
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: Restore Geodat Cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
name: Timely assets update
|
name: Scheduled assets update
|
||||||
|
|
||||||
# NOTE: This Github Actions is required by other actions, for preparing other packaging assets in a
|
# NOTE: This Github Actions is required by other actions, for preparing other packaging assets in a
|
||||||
# routine manner, for example: GeoIP/GeoSite.
|
# routine manner, for example: GeoIP/GeoSite.
|
||||||
@@ -8,19 +8,20 @@ name: Timely assets update
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
# Update assets on every hour (xx:30)
|
# Update GeoData on every day (22:30 UTC)
|
||||||
- cron: '30 * * * *'
|
- cron: '30 22 * * *'
|
||||||
push:
|
push:
|
||||||
# Prevent triggering update request storm
|
# Prevent triggering update request storm
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/hourly-prepare.yml"
|
- ".github/workflows/scheduled-assets-update.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
# Prevent triggering update request storm
|
# Prevent triggering update request storm
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/hourly-prepare.yml"
|
- ".github/workflows/scheduled-assets-update.yml"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
geodat:
|
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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Restore Geodat Cache
|
- name: Restore Geodat Cache
|
||||||
@@ -38,18 +39,18 @@ jobs:
|
|||||||
max_attempts: 60
|
max_attempts: 60
|
||||||
command: |
|
command: |
|
||||||
[ -d 'resources' ] || mkdir resources
|
[ -d 'resources' ] || mkdir resources
|
||||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
LIST=('Loyalsoldier v2ray-rules-dat geoip geoip' 'Loyalsoldier v2ray-rules-dat geosite geosite')
|
||||||
for i in "${LIST[@]}"
|
for i in "${LIST[@]}"
|
||||||
do
|
do
|
||||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3,$4}'))
|
||||||
FILE_NAME="${INFO[2]}.dat"
|
FILE_NAME="${INFO[3]}.dat"
|
||||||
echo -e "Verifying HASH key..."
|
echo -e "Verifying HASH key..."
|
||||||
HASH="$(curl -sL "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
HASH="$(curl -sL "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
|
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
|
||||||
continue
|
continue
|
||||||
else
|
else
|
||||||
echo -e "Downloading https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat..."
|
echo -e "Downloading https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat..."
|
||||||
curl -L "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat" -o ./resources/${FILE_NAME}
|
curl -L "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat" -o ./resources/${FILE_NAME}
|
||||||
echo -e "Verifying HASH key..."
|
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; }
|
[ "$(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
|
echo "unhit=true" >> $GITHUB_OUTPUT
|
37
Makefile
37
Makefile
@@ -1,37 +0,0 @@
|
|||||||
NAME = xray
|
|
||||||
|
|
||||||
VERSION=$(shell git describe --always --dirty)
|
|
||||||
|
|
||||||
# NOTE: This MAKEFILE can be used to build Xray-core locally and in Automatic workflows. It is \
|
|
||||||
provided for convenience in automatic building and functions as a part of it.
|
|
||||||
# NOTE: If you need to modify this file, please be aware that:\
|
|
||||||
- This file is not the main Makefile; it only accepts environment variables and builds the \
|
|
||||||
binary.\
|
|
||||||
- Automatic building expects the correct binaries to be built by this Makefile. If you \
|
|
||||||
intend to propose a change to this Makefile, carefully review the file below and ensure \
|
|
||||||
that the change will not accidentally break the automatic building:\
|
|
||||||
.github/workflows/release.yml \
|
|
||||||
Otherwise it is recommended to contact the project maintainers.
|
|
||||||
|
|
||||||
LDFLAGS = -X github.com/xtls/xray-core/core.build=$(VERSION) -s -w -buildid=
|
|
||||||
PARAMS = -trimpath -ldflags "$(LDFLAGS)" -v
|
|
||||||
MAIN = ./main
|
|
||||||
PREFIX ?= $(shell go env GOPATH)
|
|
||||||
ifeq ($(GOOS),windows)
|
|
||||||
OUTPUT = $(NAME).exe
|
|
||||||
ADDITION = go build -o w$(NAME).exe -trimpath -ldflags "-H windowsgui $(LDFLAGS)" -v $(MAIN)
|
|
||||||
else
|
|
||||||
OUTPUT = $(NAME)
|
|
||||||
endif
|
|
||||||
ifeq ($(shell echo "$(GOARCH)" | grep -Eq "(mips|mipsle)" && echo true),true) #
|
|
||||||
ADDITION = GOMIPS=softfloat go build -o $(NAME)_softfloat -trimpath -ldflags "$(LDFLAGS)" -v $(MAIN)
|
|
||||||
endif
|
|
||||||
.PHONY: clean build
|
|
||||||
|
|
||||||
build:
|
|
||||||
go build -o $(OUTPUT) $(PARAMS) $(MAIN)
|
|
||||||
$(ADDITION)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
go clean -v -i $(PWD)
|
|
||||||
rm -f xray xray.exe wxray.exe xray_softfloat
|
|
17
README.md
17
README.md
@@ -24,7 +24,9 @@
|
|||||||
|
|
||||||
[Project X Channel](https://t.me/projectXtls)
|
[Project X Channel](https://t.me/projectXtls)
|
||||||
|
|
||||||
[Project VLESS](https://t.me/projectVless) (non-Chinese)
|
[Project VLESS](https://t.me/projectVless) (Русский)
|
||||||
|
|
||||||
|
[Project XHTTP](https://t.me/projectXhttp) (Persian)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -72,6 +74,8 @@
|
|||||||
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
|
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
|
||||||
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
|
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
|
||||||
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
||||||
|
- Asuswrt-Merlin
|
||||||
|
- [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui)
|
||||||
- Windows
|
- Windows
|
||||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||||
- [Furious](https://github.com/LorenEteval/Furious)
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
@@ -81,6 +85,7 @@
|
|||||||
- [X-flutter](https://github.com/XTLS/X-flutter)
|
- [X-flutter](https://github.com/XTLS/X-flutter)
|
||||||
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
|
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
|
||||||
- iOS & macOS arm64
|
- iOS & macOS arm64
|
||||||
|
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
|
||||||
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
||||||
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
||||||
- macOS arm64 & x64
|
- macOS arm64 & x64
|
||||||
@@ -122,25 +127,27 @@
|
|||||||
- [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases).
|
- [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases).
|
||||||
- For third-party projects used in [Xray-core](https://github.com/XTLS/Xray-core), check your local or [the latest go.mod](https://github.com/XTLS/Xray-core/blob/main/go.mod).
|
- For third-party projects used in [Xray-core](https://github.com/XTLS/Xray-core), check your local or [the latest go.mod](https://github.com/XTLS/Xray-core/blob/main/go.mod).
|
||||||
|
|
||||||
## Compilation
|
## One-line Compilation
|
||||||
|
|
||||||
### Windows (PowerShell)
|
### Windows (PowerShell)
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$env:CGO_ENABLED=0
|
$env:CGO_ENABLED=0
|
||||||
go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main
|
go build -o xray.exe -trimpath -buildvcs=false -ldflags="-s -w -buildid=" -v ./main
|
||||||
```
|
```
|
||||||
|
|
||||||
### Linux / macOS
|
### Linux / macOS
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-s -w -buildid=" -v ./main
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reproducible Releases
|
### Reproducible Releases
|
||||||
|
|
||||||
|
Make sure that you are using the same Go version, and remember to set the git commit id (7 bytes):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make
|
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
|
||||||
|
@@ -45,11 +45,13 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
|
|||||||
case strings.EqualFold(u.String(), "localhost"):
|
case strings.EqualFold(u.String(), "localhost"):
|
||||||
return NewLocalNameServer(queryStrategy), nil
|
return NewLocalNameServer(queryStrategy), nil
|
||||||
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
|
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
|
||||||
return NewDoHNameServer(u, dispatcher, queryStrategy, false)
|
return NewDoHNameServer(u, queryStrategy, dispatcher, false), nil
|
||||||
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
|
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
|
||||||
return NewDoHNameServer(u, dispatcher, queryStrategy, true)
|
return NewDoHNameServer(u, queryStrategy, dispatcher, true), nil
|
||||||
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
|
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
|
||||||
return NewDoHLocalNameServer(u, queryStrategy), nil
|
return NewDoHNameServer(u, queryStrategy, nil, false), nil
|
||||||
|
case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
|
||||||
|
return NewDoHNameServer(u, queryStrategy, nil, true), 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, queryStrategy)
|
return NewQUICNameServer(u, queryStrategy)
|
||||||
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
|
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
|
||||||
|
@@ -8,10 +8,13 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"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/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"
|
||||||
@@ -31,7 +34,6 @@ import (
|
|||||||
// 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 {
|
||||||
dispatcher routing.Dispatcher
|
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
ips map[string]*record
|
ips map[string]*record
|
||||||
pub *pubsub.Service
|
pub *pubsub.Service
|
||||||
@@ -42,29 +44,50 @@ type DoHNameServer struct {
|
|||||||
queryStrategy QueryStrategy
|
queryStrategy QueryStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHNameServer creates DOH server object for remote resolving.
|
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
|
||||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy QueryStrategy, h2c bool) (*DoHNameServer, error) {
|
func NewDoHNameServer(url *url.URL, queryStrategy QueryStrategy, dispatcher routing.Dispatcher, h2c bool) *DoHNameServer {
|
||||||
url.Scheme = "https"
|
url.Scheme = "https"
|
||||||
errors.LogInfo(context.Background(), "DNS: created Remote DNS-over-HTTPS client for ", url.String(), ", with h2c ", h2c)
|
mode := "DOH"
|
||||||
s := baseDOHNameServer(url, "DOH", queryStrategy)
|
if dispatcher == nil {
|
||||||
|
mode = "DOHL"
|
||||||
s.dispatcher = dispatcher
|
}
|
||||||
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c)
|
||||||
|
s := &DoHNameServer{
|
||||||
|
ips: make(map[string]*record),
|
||||||
|
pub: pubsub.NewService(),
|
||||||
|
name: mode + "//" + url.Host,
|
||||||
|
dohURL: url.String(),
|
||||||
|
queryStrategy: queryStrategy,
|
||||||
|
}
|
||||||
|
s.cleanup = &task.Periodic{
|
||||||
|
Interval: time.Minute,
|
||||||
|
Execute: s.Cleanup,
|
||||||
|
}
|
||||||
|
s.httpClient = &http.Client{
|
||||||
|
Transport: &http2.Transport{
|
||||||
|
IdleConnTimeout: net.ConnIdleTimeout,
|
||||||
|
ReadIdleTimeout: net.ChromeH2KeepAlivePeriod,
|
||||||
|
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||||
dest, err := net.ParseDestination(network + ":" + addr)
|
dest, err := net.ParseDestination(network + ":" + addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
link, err := s.dispatcher.Dispatch(toDnsContext(ctx, s.dohURL), dest)
|
var conn net.Conn
|
||||||
|
if dispatcher != nil {
|
||||||
|
dnsCtx := toDnsContext(ctx, s.dohURL)
|
||||||
|
if h2c {
|
||||||
|
dnsCtx = session.ContextWithMitmAlpn11(dnsCtx, false) // for insurance
|
||||||
|
dnsCtx = session.ContextWithMitmServerName(dnsCtx, url.Hostname())
|
||||||
|
}
|
||||||
|
link, err := dispatcher.Dispatch(dnsCtx, dest)
|
||||||
select {
|
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{}
|
cc := common.ChainedClosable{}
|
||||||
if cw, ok := link.Writer.(common.Closable); ok {
|
if cw, ok := link.Writer.(common.Closable); ok {
|
||||||
cc = append(cc, cw)
|
cc = append(cc, cw)
|
||||||
@@ -72,79 +95,32 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy
|
|||||||
if cr, ok := link.Reader.(common.Closable); ok {
|
if cr, ok := link.Reader.(common.Closable); ok {
|
||||||
cc = append(cc, cr)
|
cc = append(cc, cr)
|
||||||
}
|
}
|
||||||
return cnc.NewConnection(
|
conn = cnc.NewConnection(
|
||||||
cnc.ConnectionInputMulti(link.Writer),
|
cnc.ConnectionInputMulti(link.Writer),
|
||||||
cnc.ConnectionOutputMulti(link.Reader),
|
cnc.ConnectionOutputMulti(link.Reader),
|
||||||
cnc.ConnectionOnClose(cc),
|
cnc.ConnectionOnClose(cc),
|
||||||
), nil
|
)
|
||||||
}
|
} else {
|
||||||
|
|
||||||
s.httpClient = &http.Client{
|
|
||||||
Timeout: time.Second * 180,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
MaxIdleConns: 30,
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 30 * time.Second,
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
DialContext: dialContext,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if h2c {
|
|
||||||
s.httpClient.Transport = &http2.Transport{
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
|
||||||
return dialContext(ctx, network, addr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDoHLocalNameServer creates DOH client object for local resolving
|
|
||||||
func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy) *DoHNameServer {
|
|
||||||
url.Scheme = "https"
|
|
||||||
s := baseDOHNameServer(url, "DOHL", queryStrategy)
|
|
||||||
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{
|
log.Record(&log.AccessMessage{
|
||||||
From: "DNS",
|
From: "DNS",
|
||||||
To: s.dohURL,
|
To: s.dohURL,
|
||||||
Status: log.AccessAccepted,
|
Status: log.AccessAccepted,
|
||||||
Detour: "local",
|
Detour: "local",
|
||||||
})
|
})
|
||||||
|
conn, err = internet.DialSystem(ctx, dest, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
return conn, nil
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
s.httpClient = &http.Client{
|
|
||||||
Timeout: time.Second * 180,
|
|
||||||
Transport: tr,
|
|
||||||
}
|
|
||||||
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-HTTPS client for ", url.String())
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func baseDOHNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy) *DoHNameServer {
|
|
||||||
s := &DoHNameServer{
|
|
||||||
ips: make(map[string]*record),
|
|
||||||
pub: pubsub.NewService(),
|
|
||||||
name: prefix + "//" + url.Host,
|
|
||||||
dohURL: url.String(),
|
|
||||||
queryStrategy: queryStrategy,
|
|
||||||
}
|
|
||||||
s.cleanup = &task.Periodic{
|
|
||||||
Interval: time.Minute,
|
|
||||||
Execute: s.Cleanup,
|
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@@ -305,6 +281,8 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
|||||||
req.Header.Add("Accept", "application/dns-message")
|
req.Header.Add("Accept", "application/dns-message")
|
||||||
req.Header.Add("Content-Type", "application/dns-message")
|
req.Header.Add("Content-Type", "application/dns-message")
|
||||||
|
|
||||||
|
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))
|
||||||
|
@@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
|
|||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
|
s := NewDoHNameServer(url, QueryStrategy_USE_IP, nil, false)
|
||||||
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", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
@@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
|
|||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
|
s := NewDoHNameServer(url, QueryStrategy_USE_IP, nil, false)
|
||||||
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", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
@@ -62,7 +62,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
|
|||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP4)
|
s := NewDoHNameServer(url, QueryStrategy_USE_IP4, nil, false)
|
||||||
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", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
@@ -85,7 +85,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
|
|||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP6)
|
s := NewDoHNameServer(url, QueryStrategy_USE_IP6, nil, false)
|
||||||
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", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
|
@@ -28,6 +28,7 @@ type Config struct {
|
|||||||
|
|
||||||
// Tag of the outbound handler that handles metrics http connections.
|
// Tag of the outbound handler that handles metrics http 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"`
|
||||||
|
Listen string `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -67,20 +68,28 @@ func (x *Config) GetTag() string {
|
|||||||
return ""
|
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 protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_app_metrics_config_proto_rawDesc = []byte{
|
var file_app_metrics_config_proto_rawDesc = []byte{
|
||||||
0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x63, 0x6f,
|
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,
|
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x1a, 0x0a, 0x06,
|
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,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e,
|
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74,
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
|
0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||||
0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
|
0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68,
|
||||||
0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79,
|
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72,
|
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74,
|
||||||
|
0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -10,4 +10,5 @@ option java_multiple_files = true;
|
|||||||
message Config {
|
message Config {
|
||||||
// Tag of the outbound handler that handles metrics http connections.
|
// Tag of the outbound handler that handles metrics http connections.
|
||||||
string tag = 1;
|
string tag = 1;
|
||||||
|
string listen = 2;
|
||||||
}
|
}
|
||||||
|
@@ -24,12 +24,15 @@ type MetricsHandler struct {
|
|||||||
statsManager feature_stats.Manager
|
statsManager feature_stats.Manager
|
||||||
observatory extension.Observatory
|
observatory extension.Observatory
|
||||||
tag string
|
tag string
|
||||||
|
listen string
|
||||||
|
tcpListener net.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMetricsHandler creates a new MetricsHandler based on the given config.
|
// NewMetricsHandler creates a new MetricsHandler based on the given config.
|
||||||
func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) {
|
func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) {
|
||||||
c := &MetricsHandler{
|
c := &MetricsHandler{
|
||||||
tag: config.Tag,
|
tag: config.Tag,
|
||||||
|
listen: config.Listen,
|
||||||
}
|
}
|
||||||
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) {
|
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) {
|
||||||
c.statsManager = sm
|
c.statsManager = sm
|
||||||
@@ -87,6 +90,23 @@ func (p *MetricsHandler) Type() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *MetricsHandler) Start() error {
|
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{
|
listener := &OutboundListener{
|
||||||
buffer: make(chan net.Conn, 4),
|
buffer: make(chan net.Conn, 4),
|
||||||
done: done.New(),
|
done: done.New(),
|
||||||
|
@@ -23,7 +23,7 @@ type DynamicInboundHandler struct {
|
|||||||
receiverConfig *proxyman.ReceiverConfig
|
receiverConfig *proxyman.ReceiverConfig
|
||||||
streamSettings *internet.MemoryStreamConfig
|
streamSettings *internet.MemoryStreamConfig
|
||||||
portMutex sync.Mutex
|
portMutex sync.Mutex
|
||||||
portsInUse map[net.Port]bool
|
portsInUse map[net.Port]struct{}
|
||||||
workerMutex sync.RWMutex
|
workerMutex sync.RWMutex
|
||||||
worker []worker
|
worker []worker
|
||||||
lastRefresh time.Time
|
lastRefresh time.Time
|
||||||
@@ -39,7 +39,7 @@ func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *p
|
|||||||
tag: tag,
|
tag: tag,
|
||||||
proxyConfig: proxyConfig,
|
proxyConfig: proxyConfig,
|
||||||
receiverConfig: receiverConfig,
|
receiverConfig: receiverConfig,
|
||||||
portsInUse: make(map[net.Port]bool),
|
portsInUse: make(map[net.Port]struct{}),
|
||||||
mux: mux.NewServer(ctx),
|
mux: mux.NewServer(ctx),
|
||||||
v: v,
|
v: v,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
@@ -84,7 +84,7 @@ func (h *DynamicInboundHandler) allocatePort() net.Port {
|
|||||||
port := net.Port(allPorts[r])
|
port := net.Port(allPorts[r])
|
||||||
_, used := h.portsInUse[port]
|
_, used := h.portsInUse[port]
|
||||||
if !used {
|
if !used {
|
||||||
h.portsInUse[port] = true
|
h.portsInUse[port] = struct{}{}
|
||||||
return port
|
return port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package inbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -325,6 +324,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||||||
if w.sniffingConfig != nil {
|
if w.sniffingConfig != nil {
|
||||||
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
||||||
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||||
|
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||||
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||||
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
|
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
|
||||||
}
|
}
|
||||||
@@ -464,19 +464,8 @@ func (w *dsWorker) callback(conn stat.Connection) {
|
|||||||
WriteCounter: w.downlinkCounter,
|
WriteCounter: w.downlinkCounter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// For most of time, unix obviously have no source addr. But if we leave it empty, it will cause panic.
|
|
||||||
// So we use gateway as source for log.
|
|
||||||
// However, there are some special situations where a valid source address might be available.
|
|
||||||
// Such as the source address parsed from X-Forwarded-For in websocket.
|
|
||||||
// In that case, we keep it.
|
|
||||||
var source net.Destination
|
|
||||||
if !strings.Contains(conn.RemoteAddr().String(), "unix") {
|
|
||||||
source = net.DestinationFromAddr(conn.RemoteAddr())
|
|
||||||
} else {
|
|
||||||
source = net.UnixDestination(w.address)
|
|
||||||
}
|
|
||||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||||
Source: source,
|
Source: net.DestinationFromAddr(conn.RemoteAddr()),
|
||||||
Gateway: net.UnixDestination(w.address),
|
Gateway: net.UnixDestination(w.address),
|
||||||
Tag: w.tag,
|
Tag: w.tag,
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
|
@@ -273,7 +273,16 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
|||||||
outbounds := session.OutboundsFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
ob := outbounds[len(outbounds)-1]
|
ob := outbounds[len(outbounds)-1]
|
||||||
if h.senderSettings.ViaCidr == "" {
|
if h.senderSettings.ViaCidr == "" {
|
||||||
|
if h.senderSettings.Via.AsAddress().Family().IsDomain() && h.senderSettings.Via.AsAddress().Domain() == "origin" {
|
||||||
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
|
origin, _, err := net.SplitHostPort(inbound.Conn.LocalAddr().String())
|
||||||
|
if err == nil {
|
||||||
|
ob.Gateway = net.ParseAddress(origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
ob.Gateway = h.senderSettings.Via.AsAddress()
|
ob.Gateway = h.senderSettings.Via.AsAddress()
|
||||||
|
}
|
||||||
} else { //Get a random address.
|
} else { //Get a random address.
|
||||||
ob.Gateway = ParseRandomIPv6(h.senderSettings.Via.AsAddress(), h.senderSettings.ViaCidr)
|
ob.Gateway = ParseRandomIPv6(h.senderSettings.Via.AsAddress(), h.senderSettings.ViaCidr)
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,24 @@ func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsReque
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) {
|
||||||
|
c := s.stats.GetOnlineMap(request.Name)
|
||||||
|
|
||||||
|
if c == nil {
|
||||||
|
return nil, errors.New(request.Name, " not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
ips := make(map[string]int64)
|
||||||
|
for ip, t := range c.IpTimeMap() {
|
||||||
|
ips[ip] = t.Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GetStatsOnlineIpListResponse{
|
||||||
|
Name: request.Name,
|
||||||
|
Ips: ips,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
|
func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
|
||||||
matcher, err := strmatcher.Substr.New(request.Pattern)
|
matcher, err := strmatcher.Substr.New(request.Pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -424,6 +424,59 @@ func (x *SysStatsResponse) GetUptime() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetStatsOnlineIpListResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Ips map[string]int64 `protobuf:"bytes,2,rep,name=ips,proto3" json:"ips,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetStatsOnlineIpListResponse) Reset() {
|
||||||
|
*x = GetStatsOnlineIpListResponse{}
|
||||||
|
mi := &file_app_stats_command_command_proto_msgTypes[7]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetStatsOnlineIpListResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GetStatsOnlineIpListResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GetStatsOnlineIpListResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_stats_command_command_proto_msgTypes[7]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use GetStatsOnlineIpListResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GetStatsOnlineIpListResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_stats_command_command_proto_rawDescGZIP(), []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetStatsOnlineIpListResponse) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetStatsOnlineIpListResponse) GetIps() map[string]int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Ips
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -432,7 +485,7 @@ type Config struct {
|
|||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
*x = Config{}
|
*x = Config{}
|
||||||
mi := &file_app_stats_command_command_proto_msgTypes[7]
|
mi := &file_app_stats_command_command_proto_msgTypes[8]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -444,7 +497,7 @@ func (x *Config) String() string {
|
|||||||
func (*Config) ProtoMessage() {}
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_stats_command_command_proto_msgTypes[7]
|
mi := &file_app_stats_command_command_proto_msgTypes[8]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -457,7 +510,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
return file_app_stats_command_command_proto_rawDescGZIP(), []int{7}
|
return file_app_stats_command_command_proto_rawDescGZIP(), []int{8}
|
||||||
}
|
}
|
||||||
|
|
||||||
var File_app_stats_command_command_proto protoreflect.FileDescriptor
|
var File_app_stats_command_command_proto protoreflect.FileDescriptor
|
||||||
@@ -506,40 +559,60 @@ var file_app_stats_command_command_proto_rawDesc = []byte{
|
|||||||
0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x50,
|
0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x50,
|
||||||
0x61, 0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55,
|
0x61, 0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55,
|
||||||
0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x70, 0x74,
|
0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x70, 0x74,
|
||||||
0x69, 0x6d, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xa1, 0x03,
|
0x69, 0x6d, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73,
|
||||||
0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f,
|
0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70,
|
||||||
0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61,
|
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18,
|
||||||
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
|
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73,
|
0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47,
|
||||||
0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74,
|
0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c,
|
||||||
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x70, 0x73, 0x45,
|
||||||
0x65, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e,
|
0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x69, 0x70, 0x73, 0x1a, 0x36, 0x0a, 0x08, 0x49, 0x70, 0x73,
|
||||||
0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61,
|
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||||
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||||
|
0x01, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0x9a, 0x04, 0x0a, 0x0c,
|
||||||
|
0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08,
|
||||||
|
0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
|
0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
|
||||||
|
0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
|
0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61,
|
||||||
0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74,
|
0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74,
|
||||||
0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61,
|
0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a,
|
||||||
|
0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12,
|
||||||
|
0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73,
|
||||||
|
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74,
|
||||||
|
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
|
0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
|
||||||
|
0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||||
|
0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61,
|
||||||
|
0x74, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74,
|
||||||
|
0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72,
|
||||||
|
0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e,
|
||||||
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74,
|
||||||
|
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x47,
|
||||||
|
0x65, 0x74, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||||
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
|
0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
|
0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73,
|
||||||
0x74, 0x61, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73,
|
||||||
0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75,
|
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
||||||
0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
0x77, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e,
|
||||||
0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73,
|
0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
||||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74,
|
0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
|
||||||
0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a,
|
0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x0b, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78,
|
0x1a, 0x34, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74,
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f,
|
0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
|
||||||
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e,
|
||||||
0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63,
|
||||||
0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||||
0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||||
0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50,
|
0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f,
|
||||||
0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
|
||||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70,
|
0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06,
|
||||||
0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,
|
|
||||||
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -554,7 +627,7 @@ func file_app_stats_command_command_proto_rawDescGZIP() []byte {
|
|||||||
return file_app_stats_command_command_proto_rawDescData
|
return file_app_stats_command_command_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||||
var file_app_stats_command_command_proto_goTypes = []any{
|
var file_app_stats_command_command_proto_goTypes = []any{
|
||||||
(*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest
|
(*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest
|
||||||
(*Stat)(nil), // 1: xray.app.stats.command.Stat
|
(*Stat)(nil), // 1: xray.app.stats.command.Stat
|
||||||
@@ -563,24 +636,29 @@ var file_app_stats_command_command_proto_goTypes = []any{
|
|||||||
(*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse
|
(*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse
|
||||||
(*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest
|
(*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest
|
||||||
(*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse
|
(*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse
|
||||||
(*Config)(nil), // 7: xray.app.stats.command.Config
|
(*GetStatsOnlineIpListResponse)(nil), // 7: xray.app.stats.command.GetStatsOnlineIpListResponse
|
||||||
|
(*Config)(nil), // 8: xray.app.stats.command.Config
|
||||||
|
nil, // 9: xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry
|
||||||
}
|
}
|
||||||
var file_app_stats_command_command_proto_depIdxs = []int32{
|
var file_app_stats_command_command_proto_depIdxs = []int32{
|
||||||
1, // 0: xray.app.stats.command.GetStatsResponse.stat:type_name -> xray.app.stats.command.Stat
|
1, // 0: xray.app.stats.command.GetStatsResponse.stat:type_name -> xray.app.stats.command.Stat
|
||||||
1, // 1: xray.app.stats.command.QueryStatsResponse.stat:type_name -> xray.app.stats.command.Stat
|
1, // 1: xray.app.stats.command.QueryStatsResponse.stat:type_name -> xray.app.stats.command.Stat
|
||||||
0, // 2: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest
|
9, // 2: xray.app.stats.command.GetStatsOnlineIpListResponse.ips:type_name -> xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry
|
||||||
0, // 3: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest
|
0, // 3: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest
|
||||||
3, // 4: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest
|
0, // 4: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest
|
||||||
5, // 5: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest
|
3, // 5: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest
|
||||||
2, // 6: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse
|
5, // 6: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest
|
||||||
2, // 7: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse
|
0, // 7: xray.app.stats.command.StatsService.GetStatsOnlineIpList:input_type -> xray.app.stats.command.GetStatsRequest
|
||||||
4, // 8: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse
|
2, // 8: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse
|
||||||
6, // 9: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse
|
2, // 9: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse
|
||||||
6, // [6:10] is the sub-list for method output_type
|
4, // 10: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse
|
||||||
2, // [2:6] is the sub-list for method input_type
|
6, // 11: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse
|
||||||
2, // [2:2] is the sub-list for extension type_name
|
7, // 12: xray.app.stats.command.StatsService.GetStatsOnlineIpList:output_type -> xray.app.stats.command.GetStatsOnlineIpListResponse
|
||||||
2, // [2:2] is the sub-list for extension extendee
|
8, // [8:13] is the sub-list for method output_type
|
||||||
0, // [0:2] is the sub-list for field type_name
|
3, // [3:8] is the sub-list for method input_type
|
||||||
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
|
3, // [3:3] is the sub-list for extension extendee
|
||||||
|
0, // [0:3] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_stats_command_command_proto_init() }
|
func init() { file_app_stats_command_command_proto_init() }
|
||||||
@@ -594,7 +672,7 @@ func file_app_stats_command_command_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_app_stats_command_command_proto_rawDesc,
|
RawDescriptor: file_app_stats_command_command_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 8,
|
NumMessages: 10,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
@@ -46,11 +46,17 @@ message SysStatsResponse {
|
|||||||
uint32 Uptime = 10;
|
uint32 Uptime = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetStatsOnlineIpListResponse {
|
||||||
|
string name = 1;
|
||||||
|
map<string, int64> ips = 2;
|
||||||
|
}
|
||||||
|
|
||||||
service StatsService {
|
service StatsService {
|
||||||
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
|
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
|
||||||
rpc GetStatsOnline(GetStatsRequest) returns (GetStatsResponse) {}
|
rpc GetStatsOnline(GetStatsRequest) returns (GetStatsResponse) {}
|
||||||
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
|
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
|
||||||
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
|
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
|
||||||
|
rpc GetStatsOnlineIpList(GetStatsRequest) returns (GetStatsOnlineIpListResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {}
|
message Config {}
|
||||||
|
@@ -23,6 +23,7 @@ const (
|
|||||||
StatsService_GetStatsOnline_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnline"
|
StatsService_GetStatsOnline_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnline"
|
||||||
StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats"
|
StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats"
|
||||||
StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats"
|
StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats"
|
||||||
|
StatsService_GetStatsOnlineIpList_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnlineIpList"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StatsServiceClient is the client API for StatsService service.
|
// StatsServiceClient is the client API for StatsService service.
|
||||||
@@ -33,6 +34,7 @@ type StatsServiceClient interface {
|
|||||||
GetStatsOnline(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error)
|
GetStatsOnline(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error)
|
||||||
QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error)
|
QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error)
|
||||||
GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error)
|
GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error)
|
||||||
|
GetStatsOnlineIpList(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsOnlineIpListResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type statsServiceClient struct {
|
type statsServiceClient struct {
|
||||||
@@ -83,6 +85,16 @@ func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsReques
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *statsServiceClient) GetStatsOnlineIpList(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsOnlineIpListResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(GetStatsOnlineIpListResponse)
|
||||||
|
err := c.cc.Invoke(ctx, StatsService_GetStatsOnlineIpList_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StatsServiceServer is the server API for StatsService service.
|
// StatsServiceServer is the server API for StatsService service.
|
||||||
// All implementations must embed UnimplementedStatsServiceServer
|
// All implementations must embed UnimplementedStatsServiceServer
|
||||||
// for forward compatibility.
|
// for forward compatibility.
|
||||||
@@ -91,6 +103,7 @@ type StatsServiceServer interface {
|
|||||||
GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
||||||
QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error)
|
QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error)
|
||||||
GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error)
|
GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error)
|
||||||
|
GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error)
|
||||||
mustEmbedUnimplementedStatsServiceServer()
|
mustEmbedUnimplementedStatsServiceServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +126,9 @@ func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRe
|
|||||||
func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) {
|
func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedStatsServiceServer) GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetStatsOnlineIpList not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
|
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
|
||||||
func (UnimplementedStatsServiceServer) testEmbeddedByValue() {}
|
func (UnimplementedStatsServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
@@ -206,6 +222,24 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _StatsService_GetStatsOnlineIpList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetStatsRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StatsService_GetStatsOnlineIpList_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, req.(*GetStatsRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService service.
|
// StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService 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)
|
||||||
@@ -229,6 +263,10 @@ var StatsService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "GetSysStats",
|
MethodName: "GetSysStats",
|
||||||
Handler: _StatsService_GetSysStats_Handler,
|
Handler: _StatsService_GetSysStats_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetStatsOnlineIpList",
|
||||||
|
Handler: _StatsService_GetStatsOnlineIpList_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "app/stats/command/command.proto",
|
Metadata: "app/stats/command/command.proto",
|
||||||
|
@@ -78,3 +78,13 @@ func (c *OnlineMap) RemoveExpiredIPs(list map[string]time.Time) map[string]time.
|
|||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *OnlineMap) IpTimeMap() map[string]time.Time {
|
||||||
|
list := c.ipList
|
||||||
|
if time.Since(c.lastCleanup) > c.cleanupPeriod {
|
||||||
|
list = c.RemoveExpiredIPs(list)
|
||||||
|
c.lastCleanup = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.ipList
|
||||||
|
}
|
||||||
|
@@ -1,2 +1,15 @@
|
|||||||
// Package crypto provides common crypto libraries for Xray.
|
// Package crypto provides common crypto libraries for Xray.
|
||||||
package crypto // import "github.com/xtls/xray-core/common/crypto"
|
package crypto // import "github.com/xtls/xray-core/common/crypto"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RandBetween(from int64, to int64) int64 {
|
||||||
|
if from == to {
|
||||||
|
return from
|
||||||
|
}
|
||||||
|
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||||
|
return from + bigInt.Int64()
|
||||||
|
}
|
||||||
|
@@ -120,7 +120,7 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu
|
|||||||
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
// deep-clone outbounds because it is going to be mutated concurrently
|
// deep-clone outbounds because it is going to be mutated concurrently
|
||||||
// (Target and OriginalTarget)
|
// (Target and OriginalTarget)
|
||||||
ctx = session.ContextCloneOutbounds(ctx)
|
ctx = session.ContextCloneOutboundsAndContent(ctx)
|
||||||
errors.LogInfo(ctx, "received request for ", meta.Target)
|
errors.LogInfo(ctx, "received request for ", meta.Target)
|
||||||
{
|
{
|
||||||
msg := &log.AccessMessage{
|
msg := &log.AccessMessage{
|
||||||
|
@@ -89,13 +89,11 @@ func UnixDestination(address Address) Destination {
|
|||||||
// NetAddr returns the network address in this Destination in string form.
|
// NetAddr returns the network address in this Destination in string form.
|
||||||
func (d Destination) NetAddr() string {
|
func (d Destination) NetAddr() string {
|
||||||
addr := ""
|
addr := ""
|
||||||
if d.Address != nil {
|
|
||||||
if d.Network == Network_TCP || d.Network == Network_UDP {
|
if d.Network == Network_TCP || d.Network == Network_UDP {
|
||||||
addr = d.Address.String() + ":" + d.Port.String()
|
addr = d.Address.String() + ":" + d.Port.String()
|
||||||
} else if d.Network == Network_UNIX {
|
} else if d.Network == Network_UNIX {
|
||||||
addr = d.Address.String()
|
addr = d.Address.String()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,2 +1,14 @@
|
|||||||
// Package net is a drop-in replacement to Golang's net package, with some more functionalities.
|
// Package net is a drop-in replacement to Golang's net package, with some more functionalities.
|
||||||
package net // import "github.com/xtls/xray-core/common/net"
|
package net // import "github.com/xtls/xray-core/common/net"
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// defines the maximum time an idle TCP session can survive in the tunnel, so
|
||||||
|
// it should be consistent across HTTP versions and with other transports.
|
||||||
|
const ConnIdleTimeout = 300 * time.Second
|
||||||
|
|
||||||
|
// consistent with quic-go
|
||||||
|
const QuicgoH3KeepAlivePeriod = 10 * time.Second
|
||||||
|
|
||||||
|
// consistent with chrome
|
||||||
|
const ChromeH2KeepAlivePeriod = 45 * time.Second
|
||||||
|
@@ -76,8 +76,9 @@ type (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ResolveUnixAddr = net.ResolveUnixAddr
|
ResolveTCPAddr = net.ResolveTCPAddr
|
||||||
ResolveUDPAddr = net.ResolveUDPAddr
|
ResolveUDPAddr = net.ResolveUDPAddr
|
||||||
|
ResolveUnixAddr = net.ResolveUnixAddr
|
||||||
)
|
)
|
||||||
|
|
||||||
type Resolver = net.Resolver
|
type Resolver = net.Resolver
|
||||||
|
@@ -63,7 +63,7 @@ func SniffHTTP(b []byte, c context.Context) (*SniffHeader, error) {
|
|||||||
ShouldSniffAttr := true
|
ShouldSniffAttr := true
|
||||||
// If content.Attributes have information, that means it comes from HTTP inbound PlainHTTP mode.
|
// If content.Attributes have information, that means it comes from HTTP inbound PlainHTTP mode.
|
||||||
// It will set attributes, so skip it.
|
// It will set attributes, so skip it.
|
||||||
if content == nil || content.AttributeLen() != 0 {
|
if content == nil || len(content.Attributes) != 0 {
|
||||||
ShouldSniffAttr = false
|
ShouldSniffAttr = false
|
||||||
}
|
}
|
||||||
if err := beginWithHTTPMethod(b); err != nil {
|
if err := beginWithHTTPMethod(b); err != nil {
|
||||||
|
3
common/protocol/tls/cert/.gitignore
vendored
3
common/protocol/tls/cert/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
*.pem
|
*.crt
|
||||||
|
*.key
|
@@ -78,9 +78,9 @@ func printJSON(certificate *Certificate) {
|
|||||||
func printFile(certificate *Certificate, name string) error {
|
func printFile(certificate *Certificate, name string) error {
|
||||||
certPEM, keyPEM := certificate.ToPEM()
|
certPEM, keyPEM := certificate.ToPEM()
|
||||||
return task.Run(context.Background(), func() error {
|
return task.Run(context.Background(), func() error {
|
||||||
return writeFile(certPEM, name+"_cert.pem")
|
return writeFile(certPEM, name+".crt")
|
||||||
}, func() error {
|
}, func() error {
|
||||||
return writeFile(keyPEM, name+"_key.pem")
|
return writeFile(keyPEM, name+".key")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,6 +23,8 @@ const (
|
|||||||
timeoutOnlyKey ctx.SessionKey = 8
|
timeoutOnlyKey ctx.SessionKey = 8
|
||||||
allowedNetworkKey ctx.SessionKey = 9
|
allowedNetworkKey ctx.SessionKey = 9
|
||||||
handlerSessionKey ctx.SessionKey = 10
|
handlerSessionKey ctx.SessionKey = 10
|
||||||
|
mitmAlpn11Key ctx.SessionKey = 11
|
||||||
|
mitmServerNameKey ctx.SessionKey = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
|
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
|
||||||
@@ -40,7 +42,7 @@ func ContextWithOutbounds(ctx context.Context, outbounds []*Outbound) context.Co
|
|||||||
return context.WithValue(ctx, outboundSessionKey, outbounds)
|
return context.WithValue(ctx, outboundSessionKey, outbounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ContextCloneOutbounds(ctx context.Context) context.Context {
|
func ContextCloneOutboundsAndContent(ctx context.Context) context.Context {
|
||||||
outbounds := OutboundsFromContext(ctx)
|
outbounds := OutboundsFromContext(ctx)
|
||||||
newOutbounds := make([]*Outbound, len(outbounds))
|
newOutbounds := make([]*Outbound, len(outbounds))
|
||||||
for i, ob := range outbounds {
|
for i, ob := range outbounds {
|
||||||
@@ -53,7 +55,15 @@ func ContextCloneOutbounds(ctx context.Context) context.Context {
|
|||||||
newOutbounds[i] = &v
|
newOutbounds[i] = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
return ContextWithOutbounds(ctx, newOutbounds)
|
content := ContentFromContext(ctx)
|
||||||
|
newContent := Content{}
|
||||||
|
if content != nil {
|
||||||
|
newContent = *content
|
||||||
|
if content.Attributes != nil {
|
||||||
|
panic("content.Attributes != nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ContextWithContent(ContextWithOutbounds(ctx, newOutbounds), &newContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OutboundsFromContext(ctx context.Context) []*Outbound {
|
func OutboundsFromContext(ctx context.Context) []*Outbound {
|
||||||
@@ -162,3 +172,25 @@ func AllowedNetworkFromContext(ctx context.Context) net.Network {
|
|||||||
}
|
}
|
||||||
return net.Network_Unknown
|
return net.Network_Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ContextWithMitmAlpn11(ctx context.Context, alpn11 bool) context.Context {
|
||||||
|
return context.WithValue(ctx, mitmAlpn11Key, alpn11)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MitmAlpn11FromContext(ctx context.Context) bool {
|
||||||
|
if val, ok := ctx.Value(mitmAlpn11Key).(bool); ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextWithMitmServerName(ctx context.Context, serverName string) context.Context {
|
||||||
|
return context.WithValue(ctx, mitmServerNameKey, serverName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MitmServerNameFromContext(ctx context.Context) string {
|
||||||
|
if val, ok := ctx.Value(mitmServerNameKey).(string); ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@@ -4,7 +4,6 @@ package session // import "github.com/xtls/xray-core/common/session"
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sync"
|
|
||||||
|
|
||||||
c "github.com/xtls/xray-core/common/ctx"
|
c "github.com/xtls/xray-core/common/ctx"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -75,8 +74,8 @@ type Outbound struct {
|
|||||||
|
|
||||||
// SniffingRequest controls the behavior of content sniffing.
|
// SniffingRequest controls the behavior of content sniffing.
|
||||||
type SniffingRequest struct {
|
type SniffingRequest struct {
|
||||||
ExcludeForDomain []string
|
ExcludeForDomain []string // read-only once set
|
||||||
OverrideDestinationForProtocol []string
|
OverrideDestinationForProtocol []string // read-only once set
|
||||||
Enabled bool
|
Enabled bool
|
||||||
MetadataOnly bool
|
MetadataOnly bool
|
||||||
RouteOnly bool
|
RouteOnly bool
|
||||||
@@ -92,10 +91,6 @@ type Content struct {
|
|||||||
Attributes map[string]string
|
Attributes map[string]string
|
||||||
|
|
||||||
SkipDNSResolve bool
|
SkipDNSResolve bool
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
|
|
||||||
isLocked bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sockopt is the settings for socket connection.
|
// Sockopt is the settings for socket connection.
|
||||||
@@ -104,22 +99,8 @@ type Sockopt struct {
|
|||||||
Mark int32
|
Mark int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some how when using mux, there will be a same ctx between different requests
|
|
||||||
// This will cause problem as it's designed for single request, like concurrent map writes
|
|
||||||
// Add a Mutex as a temp solution
|
|
||||||
|
|
||||||
// SetAttribute attaches additional string attributes to content.
|
// SetAttribute attaches additional string attributes to content.
|
||||||
func (c *Content) SetAttribute(name string, value string) {
|
func (c *Content) SetAttribute(name string, value string) {
|
||||||
if c.isLocked {
|
|
||||||
errors.LogError(context.Background(), "Multiple goroutines are tring to access one routing content, tring to write ", name, ":", value)
|
|
||||||
}
|
|
||||||
c.mu.Lock()
|
|
||||||
c.isLocked = true
|
|
||||||
defer func() {
|
|
||||||
c.isLocked = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if c.Attributes == nil {
|
if c.Attributes == nil {
|
||||||
c.Attributes = make(map[string]string)
|
c.Attributes = make(map[string]string)
|
||||||
}
|
}
|
||||||
@@ -128,24 +109,8 @@ func (c *Content) SetAttribute(name string, value string) {
|
|||||||
|
|
||||||
// Attribute retrieves additional string attributes from content.
|
// Attribute retrieves additional string attributes from content.
|
||||||
func (c *Content) Attribute(name string) string {
|
func (c *Content) Attribute(name string) string {
|
||||||
c.mu.Lock()
|
|
||||||
c.isLocked = true
|
|
||||||
defer func() {
|
|
||||||
c.isLocked = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
}()
|
|
||||||
if c.Attributes == nil {
|
if c.Attributes == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return c.Attributes[name]
|
return c.Attributes[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Content) AttributeLen() int {
|
|
||||||
c.mu.Lock()
|
|
||||||
c.isLocked = true
|
|
||||||
defer func() {
|
|
||||||
c.isLocked = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
}()
|
|
||||||
return len(c.Attributes)
|
|
||||||
}
|
|
||||||
|
@@ -18,8 +18,8 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Version_x byte = 25
|
Version_x byte = 25
|
||||||
Version_y byte = 1
|
Version_y byte = 3
|
||||||
Version_z byte = 30
|
Version_z byte = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -2,6 +2,7 @@ package stats
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"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"
|
||||||
@@ -30,6 +31,8 @@ type OnlineMap interface {
|
|||||||
AddIP(string)
|
AddIP(string)
|
||||||
// List is the current OnlineMap ip list.
|
// List is the current OnlineMap ip list.
|
||||||
List() []string
|
List() []string
|
||||||
|
// IpTimeMap return client ips and their last access time.
|
||||||
|
IpTimeMap() map[string]time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Channel is the interface for stats channel.
|
// Channel is the interface for stats channel.
|
||||||
|
26
go.mod
26
go.mod
@@ -1,18 +1,18 @@
|
|||||||
module github.com/xtls/xray-core
|
module github.com/xtls/xray-core
|
||||||
|
|
||||||
go 1.23
|
go 1.24
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
|
||||||
github.com/cloudflare/circl v1.5.0
|
github.com/cloudflare/circl v1.6.0
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
||||||
github.com/golang/mock v1.7.0-rc.1
|
github.com/golang/mock v1.7.0-rc.1
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/miekg/dns v1.1.63
|
github.com/miekg/dns v1.1.63
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
github.com/pires/go-proxyproto v0.8.0
|
github.com/pires/go-proxyproto v0.8.0
|
||||||
github.com/quic-go/quic-go v0.49.0
|
github.com/quic-go/quic-go v0.50.0
|
||||||
github.com/refraction-networking/utls v1.6.7
|
github.com/refraction-networking/utls v1.6.7
|
||||||
github.com/sagernet/sing v0.5.1
|
github.com/sagernet/sing v0.5.1
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
@@ -22,16 +22,16 @@ require (
|
|||||||
github.com/vishvananda/netlink v1.3.0
|
github.com/vishvananda/netlink v1.3.0
|
||||||
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.32.0
|
golang.org/x/crypto v0.36.0
|
||||||
golang.org/x/net v0.34.0
|
golang.org/x/net v0.37.0
|
||||||
golang.org/x/sync v0.10.0
|
golang.org/x/sync v0.12.0
|
||||||
golang.org/x/sys v0.29.0
|
golang.org/x/sys v0.31.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||||
google.golang.org/grpc v1.70.0
|
google.golang.org/grpc v1.71.0
|
||||||
google.golang.org/protobuf v1.36.4
|
google.golang.org/protobuf v1.36.5
|
||||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0
|
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0
|
||||||
h12.io/socks v1.0.3
|
h12.io/socks v1.0.3
|
||||||
lukechampine.com/blake3 v1.3.0
|
lukechampine.com/blake3 v1.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -51,11 +51,11 @@ require (
|
|||||||
go.uber.org/mock v0.5.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
||||||
golang.org/x/mod v0.21.0 // indirect
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/text v0.23.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.26.0 // indirect
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
70
go.sum
70
go.sum
@@ -2,8 +2,8 @@ github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJS
|
|||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
||||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -24,8 +24,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
|||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
|
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
@@ -54,8 +54,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
|
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
||||||
github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
|
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
@@ -79,24 +79,26 @@ github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZla
|
|||||||
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg=
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg=
|
||||||
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||||
|
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||||
|
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
@@ -105,12 +107,12 @@ golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
|||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -119,14 +121,14 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -141,12 +143,12 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
|||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
@@ -160,5 +162,5 @@ gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 h1:P+U/06iIKPQ3DLcg+zBfSCia
|
|||||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
||||||
h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
|
h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
|
||||||
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
||||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
||||||
|
@@ -203,6 +203,24 @@ func (list *PortList) Build() *net.PortList {
|
|||||||
return portList
|
return portList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v PortList) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v PortList) String() string {
|
||||||
|
ports := []string{}
|
||||||
|
for _, port := range v.Range {
|
||||||
|
if port.From == port.To {
|
||||||
|
p := strconv.Itoa(int(port.From))
|
||||||
|
ports = append(ports, p)
|
||||||
|
} else {
|
||||||
|
p := fmt.Sprintf("%d-%d", port.From, port.To)
|
||||||
|
ports = append(ports, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(ports, ",")
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
|
// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
|
||||||
func (list *PortList) UnmarshalJSON(data []byte) error {
|
func (list *PortList) UnmarshalJSON(data []byte) error {
|
||||||
var listStr string
|
var listStr string
|
||||||
|
@@ -12,13 +12,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NameServerConfig struct {
|
type NameServerConfig struct {
|
||||||
Address *Address
|
Address *Address `json:"address"`
|
||||||
ClientIP *Address
|
ClientIP *Address `json:"clientIp"`
|
||||||
Port uint16
|
Port uint16 `json:"port"`
|
||||||
SkipFallback bool
|
SkipFallback bool `json:"skipFallback"`
|
||||||
Domains []string
|
Domains []string `json:"domains"`
|
||||||
ExpectIPs StringList
|
ExpectIPs StringList `json:"expectIps"`
|
||||||
QueryStrategy string
|
QueryStrategy string `json:"queryStrategy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||||
|
@@ -7,14 +7,20 @@ import (
|
|||||||
|
|
||||||
type MetricsConfig struct {
|
type MetricsConfig struct {
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
|
Listen string `json:"listen"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetricsConfig) Build() (*metrics.Config, error) {
|
func (c *MetricsConfig) Build() (*metrics.Config, error) {
|
||||||
|
if c.Listen == "" && c.Tag == "" {
|
||||||
|
return nil, errors.New("Metrics must have a tag or listen address.")
|
||||||
|
}
|
||||||
|
// If the tag is empty but have "listen" set a default "Metrics" for compatibility.
|
||||||
if c.Tag == "" {
|
if c.Tag == "" {
|
||||||
return nil, errors.New("metrics tag can't be empty.")
|
c.Tag = "Metrics"
|
||||||
}
|
}
|
||||||
|
|
||||||
return &metrics.Config{
|
return &metrics.Config{
|
||||||
Tag: c.Tag,
|
Tag: c.Tag,
|
||||||
|
Listen: c.Listen,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -411,6 +411,7 @@ type TLSConfig struct {
|
|||||||
CurvePreferences *StringList `json:"curvePreferences"`
|
CurvePreferences *StringList `json:"curvePreferences"`
|
||||||
MasterKeyLog string `json:"masterKeyLog"`
|
MasterKeyLog string `json:"masterKeyLog"`
|
||||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||||
|
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -432,6 +433,13 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
if c.ALPN != nil && len(*c.ALPN) > 0 {
|
if c.ALPN != nil && len(*c.ALPN) > 0 {
|
||||||
config.NextProtocol = []string(*c.ALPN)
|
config.NextProtocol = []string(*c.ALPN)
|
||||||
}
|
}
|
||||||
|
if len(config.NextProtocol) > 1 {
|
||||||
|
for _, p := range config.NextProtocol {
|
||||||
|
if tcp.IsFromMitm(p) {
|
||||||
|
return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if c.CurvePreferences != nil && len(*c.CurvePreferences) > 0 {
|
if c.CurvePreferences != nil && len(*c.CurvePreferences) > 0 {
|
||||||
config.CurvePreferences = []string(*c.CurvePreferences)
|
config.CurvePreferences = []string(*c.CurvePreferences)
|
||||||
}
|
}
|
||||||
@@ -442,7 +450,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
config.CipherSuites = c.CipherSuites
|
config.CipherSuites = c.CipherSuites
|
||||||
config.Fingerprint = strings.ToLower(c.Fingerprint)
|
config.Fingerprint = strings.ToLower(c.Fingerprint)
|
||||||
if config.Fingerprint != "unsafe" && tls.GetFingerprint(config.Fingerprint) == nil {
|
if config.Fingerprint != "unsafe" && tls.GetFingerprint(config.Fingerprint) == nil {
|
||||||
return nil, errors.New(`unknown fingerprint: `, config.Fingerprint)
|
return nil, errors.New(`unknown "fingerprint": `, config.Fingerprint)
|
||||||
}
|
}
|
||||||
config.RejectUnknownSni = c.RejectUnknownSNI
|
config.RejectUnknownSni = c.RejectUnknownSNI
|
||||||
|
|
||||||
@@ -469,10 +477,11 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.MasterKeyLog = c.MasterKeyLog
|
config.MasterKeyLog = c.MasterKeyLog
|
||||||
config.ServerNameToVerify = c.ServerNameToVerify
|
|
||||||
if config.ServerNameToVerify != "" && config.Fingerprint == "unsafe" {
|
if c.ServerNameToVerify != "" {
|
||||||
return nil, errors.New(`serverNameToVerify only works with uTLS for now`)
|
return nil, errors.PrintRemovedFeatureError(`"serverNameToVerify"`, `"verifyPeerCertInNames"`)
|
||||||
}
|
}
|
||||||
|
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
@@ -493,6 +502,7 @@ type REALITYConfig struct {
|
|||||||
|
|
||||||
Fingerprint string `json:"fingerprint"`
|
Fingerprint string `json:"fingerprint"`
|
||||||
ServerName string `json:"serverName"`
|
ServerName string `json:"serverName"`
|
||||||
|
Password string `json:"password"`
|
||||||
PublicKey string `json:"publicKey"`
|
PublicKey string `json:"publicKey"`
|
||||||
ShortId string `json:"shortId"`
|
ShortId string `json:"shortId"`
|
||||||
SpiderX string `json:"spiderX"`
|
SpiderX string `json:"spiderX"`
|
||||||
@@ -601,11 +611,14 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
|||||||
if len(c.ServerNames) != 0 {
|
if len(c.ServerNames) != 0 {
|
||||||
return nil, errors.New(`non-empty "serverNames", please use "serverName" instead`)
|
return nil, errors.New(`non-empty "serverNames", please use "serverName" instead`)
|
||||||
}
|
}
|
||||||
|
if c.Password != "" {
|
||||||
|
c.PublicKey = c.Password
|
||||||
|
}
|
||||||
if c.PublicKey == "" {
|
if c.PublicKey == "" {
|
||||||
return nil, errors.New(`empty "publicKey"`)
|
return nil, errors.New(`empty "password"`)
|
||||||
}
|
}
|
||||||
if config.PublicKey, err = base64.RawURLEncoding.DecodeString(c.PublicKey); err != nil || len(config.PublicKey) != 32 {
|
if config.PublicKey, err = base64.RawURLEncoding.DecodeString(c.PublicKey); err != nil || len(config.PublicKey) != 32 {
|
||||||
return nil, errors.New(`invalid "publicKey": `, c.PublicKey)
|
return nil, errors.New(`invalid "password": `, c.PublicKey)
|
||||||
}
|
}
|
||||||
if len(c.ShortIds) != 0 {
|
if len(c.ShortIds) != 0 {
|
||||||
return nil, errors.New(`non-empty "shortIds", please use "shortId" instead`)
|
return nil, errors.New(`non-empty "shortIds", please use "shortId" instead`)
|
||||||
@@ -702,6 +715,7 @@ type SocketConfig struct {
|
|||||||
Interface string `json:"interface"`
|
Interface string `json:"interface"`
|
||||||
TcpMptcp bool `json:"tcpMptcp"`
|
TcpMptcp bool `json:"tcpMptcp"`
|
||||||
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
|
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
|
||||||
|
AddressPortStrategy string `json:"addressPortStrategy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -771,6 +785,26 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
customSockopts = append(customSockopts, customSockopt)
|
customSockopts = append(customSockopts, customSockopt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addressPortStrategy := internet.AddressPortStrategy_None
|
||||||
|
switch strings.ToLower(c.AddressPortStrategy) {
|
||||||
|
case "none", "":
|
||||||
|
addressPortStrategy = internet.AddressPortStrategy_None
|
||||||
|
case "srvportonly":
|
||||||
|
addressPortStrategy = internet.AddressPortStrategy_SrvPortOnly
|
||||||
|
case "srvaddressonly":
|
||||||
|
addressPortStrategy = internet.AddressPortStrategy_SrvAddressOnly
|
||||||
|
case "srvportandaddress":
|
||||||
|
addressPortStrategy = internet.AddressPortStrategy_SrvPortAndAddress
|
||||||
|
case "txtportonly":
|
||||||
|
addressPortStrategy = internet.AddressPortStrategy_TxtPortOnly
|
||||||
|
case "txtaddressonly":
|
||||||
|
addressPortStrategy = internet.AddressPortStrategy_TxtAddressOnly
|
||||||
|
case "txtportandaddress":
|
||||||
|
addressPortStrategy = internet.AddressPortStrategy_TxtPortAndAddress
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy)
|
||||||
|
}
|
||||||
|
|
||||||
return &internet.SocketConfig{
|
return &internet.SocketConfig{
|
||||||
Mark: c.Mark,
|
Mark: c.Mark,
|
||||||
Tfo: tfo,
|
Tfo: tfo,
|
||||||
@@ -789,6 +823,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
Interface: c.Interface,
|
Interface: c.Interface,
|
||||||
TcpMptcp: c.TcpMptcp,
|
TcpMptcp: c.TcpMptcp,
|
||||||
CustomSockopt: customSockopts,
|
CustomSockopt: customSockopts,
|
||||||
|
AddressPortStrategy: addressPortStrategy,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -292,9 +292,11 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
|||||||
senderSettings.ViaCidr = strings.Split(*c.SendThrough, "/")[1]
|
senderSettings.ViaCidr = strings.Split(*c.SendThrough, "/")[1]
|
||||||
} else {
|
} else {
|
||||||
if address.Family().IsDomain() {
|
if address.Family().IsDomain() {
|
||||||
|
if address.Address.Domain() != "origin" {
|
||||||
return nil, errors.New("unable to send through: " + address.String())
|
return nil, errors.New("unable to send through: " + address.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
senderSettings.Via = address.Build()
|
senderSettings.Via = address.Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -89,12 +89,11 @@ func whichProtoc(suffix, targetedVersion string) (string, error) {
|
|||||||
|
|
||||||
path, err := exec.LookPath(protoc)
|
path, err := exec.LookPath(protoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errStr := fmt.Sprintf(`
|
return "", fmt.Errorf(`
|
||||||
Command "%s" not found.
|
Command "%s" not found.
|
||||||
Make sure that %s is in your system path or current path.
|
Make sure that %s is in your system path or current path.
|
||||||
Download %s v%s or later from https://github.com/protocolbuffers/protobuf/releases
|
Download %s v%s or later from https://github.com/protocolbuffers/protobuf/releases
|
||||||
`, protoc, protoc, protoc, targetedVersion)
|
`, protoc, protoc, protoc, targetedVersion)
|
||||||
return "", fmt.Errorf(errStr)
|
|
||||||
}
|
}
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
@@ -27,5 +27,6 @@ var CmdAPI = &base.Command{
|
|||||||
cmdRemoveRules,
|
cmdRemoveRules,
|
||||||
cmdSourceIpBlock,
|
cmdSourceIpBlock,
|
||||||
cmdOnlineStats,
|
cmdOnlineStats,
|
||||||
|
cmdOnlineStatsIpList,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -13,25 +13,20 @@ import (
|
|||||||
var cmdBalancerInfo = &base.Command{
|
var cmdBalancerInfo = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api bi [--server=127.0.0.1:8080] [balancer]...",
|
UsageLine: "{{.Exec}} api bi [--server=127.0.0.1:8080] [balancer]...",
|
||||||
Short: "balancer information",
|
Short: "Retrieve balancer information",
|
||||||
Long: `
|
Long: `
|
||||||
Get information of specified balancers, including health, strategy
|
Retrieve information of specified balancers, including health, strategy and selecting.
|
||||||
and selecting. If no balancer tag specified, get information of
|
If no balancer tag specified, information for all balancers is returned.
|
||||||
all balancers.
|
|
||||||
|
|
||||||
> Make sure you have "RoutingService" set in "config.api.services"
|
> Ensure that "RoutingService" is enabled under "config.api.services" in the server configuration.
|
||||||
of server config.
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
-json
|
|
||||||
Use json output.
|
|
||||||
|
|
||||||
-s, -server <server:port>
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
|
|
||||||
-t, -timeout <seconds>
|
-t, -timeout <seconds>
|
||||||
Timeout seconds to call API. Default 3
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@@ -7,31 +7,27 @@ import (
|
|||||||
|
|
||||||
var cmdBalancerOverride = &base.Command{
|
var cmdBalancerOverride = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api bo [--server=127.0.0.1:8080] <-b balancer> outboundTag",
|
UsageLine: "{{.Exec}} api bo [--server=127.0.0.1:8080] <-b balancer> outboundTag <-r>",
|
||||||
Short: "balancer override",
|
Short: "Override balancer",
|
||||||
Long: `
|
Long: `
|
||||||
Override a balancer's selection.
|
Override the selection target of a balancer.
|
||||||
|
|
||||||
> Make sure you have "RoutingService" set in "config.api.services"
|
> Ensure that the "RoutingService" is properly configured under "config.api.services" in the server configuration.
|
||||||
of server config.
|
|
||||||
|
|
||||||
Once a balancer's selecting is overridden:
|
Once the balancer's selection is overridden:
|
||||||
|
|
||||||
- The balancer's selection result will always be outboundTag
|
- The balancer's selection result will always be outboundTag
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
-r, -remove
|
-s, -server <server:port>
|
||||||
Remove the overridden
|
|
||||||
|
|
||||||
-r, -remove
|
|
||||||
Remove the override
|
|
||||||
|
|
||||||
-s, -server
|
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
|
|
||||||
-t, -timeout
|
-t, -timeout <seconds>
|
||||||
Timeout seconds to call API. Default 3
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
|
-r, -remove
|
||||||
|
Remove the existing override.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@@ -8,19 +8,27 @@ import (
|
|||||||
var cmdInboundUser = &base.Command{
|
var cmdInboundUser = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api inbounduser [--server=127.0.0.1:8080] -tag=tag [-email=email]",
|
UsageLine: "{{.Exec}} api inbounduser [--server=127.0.0.1:8080] -tag=tag [-email=email]",
|
||||||
Short: "Get Inbound User",
|
Short: "Retrieve inbound user(s)",
|
||||||
Long: `
|
Long: `
|
||||||
Get User info from an inbound.
|
Get User info from an inbound.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
-tag
|
-tag
|
||||||
Inbound tag
|
Inbound tag
|
||||||
|
|
||||||
-email
|
-email
|
||||||
User email. If email is not given, will get all users
|
The user's email address. If not provided, all users will be retrieved.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name"
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name" -email="xray@love.com"
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name" -email="xray@love.com"
|
||||||
`,
|
`,
|
||||||
Run: executeInboundUser,
|
Run: executeInboundUser,
|
||||||
|
@@ -8,17 +8,23 @@ import (
|
|||||||
var cmdInboundUserCount = &base.Command{
|
var cmdInboundUserCount = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api inboundusercount [--server=127.0.0.1:8080] -tag=tag",
|
UsageLine: "{{.Exec}} api inboundusercount [--server=127.0.0.1:8080] -tag=tag",
|
||||||
Short: "Get Inbound User Count",
|
Short: "Retrieve inbound user count",
|
||||||
Long: `
|
Long: `
|
||||||
Get User count from an inbound.
|
Retrieve the user count for a specified inbound tag.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
-tag
|
-tag
|
||||||
Inbound tag
|
Inbound tag
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name"
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name"
|
||||||
`,
|
`,
|
||||||
Run: executeInboundUserCount,
|
Run: executeInboundUserCount,
|
||||||
|
@@ -15,12 +15,17 @@ var cmdAddInbounds = &base.Command{
|
|||||||
Short: "Add inbounds",
|
Short: "Add inbounds",
|
||||||
Long: `
|
Long: `
|
||||||
Add inbounds to Xray.
|
Add inbounds to Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
||||||
`,
|
`,
|
||||||
Run: executeAddInbounds,
|
Run: executeAddInbounds,
|
||||||
|
@@ -14,12 +14,17 @@ var cmdRemoveInbounds = &base.Command{
|
|||||||
Short: "Remove inbounds",
|
Short: "Remove inbounds",
|
||||||
Long: `
|
Long: `
|
||||||
Remove inbounds from Xray.
|
Remove inbounds from Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
|
||||||
`,
|
`,
|
||||||
Run: executeRemoveInbounds,
|
Run: executeRemoveInbounds,
|
||||||
|
@@ -11,11 +11,18 @@ var cmdRestartLogger = &base.Command{
|
|||||||
Short: "Restart the logger",
|
Short: "Restart the logger",
|
||||||
Long: `
|
Long: `
|
||||||
Restart the logger of Xray.
|
Restart the logger of Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080
|
||||||
`,
|
`,
|
||||||
Run: executeRestartLogger,
|
Run: executeRestartLogger,
|
||||||
}
|
}
|
||||||
|
@@ -15,12 +15,17 @@ var cmdAddOutbounds = &base.Command{
|
|||||||
Short: "Add outbounds",
|
Short: "Add outbounds",
|
||||||
Long: `
|
Long: `
|
||||||
Add outbounds to Xray.
|
Add outbounds to Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
||||||
`,
|
`,
|
||||||
Run: executeAddOutbounds,
|
Run: executeAddOutbounds,
|
||||||
|
@@ -14,12 +14,17 @@ var cmdRemoveOutbounds = &base.Command{
|
|||||||
Short: "Remove outbounds",
|
Short: "Remove outbounds",
|
||||||
Long: `
|
Long: `
|
||||||
Remove outbounds from Xray.
|
Remove outbounds from Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
|
||||||
`,
|
`,
|
||||||
Run: executeRemoveOutbounds,
|
Run: executeRemoveOutbounds,
|
||||||
|
@@ -16,15 +16,20 @@ var cmdAddRules = &base.Command{
|
|||||||
Short: "Add routing rules",
|
Short: "Add routing rules",
|
||||||
Long: `
|
Long: `
|
||||||
Add routing rules to Xray.
|
Add routing rules to Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
|
-t, -timeout <seconds>
|
||||||
Timeout seconds to call API. Default 3
|
Timeout seconds to call API. Default 3
|
||||||
|
|
||||||
-append
|
-append
|
||||||
append or replace config. Default false
|
Append to the existing configuration instead of replacing it. Default false
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
||||||
`,
|
`,
|
||||||
Run: executeAddRules,
|
Run: executeAddRules,
|
||||||
|
@@ -9,16 +9,21 @@ import (
|
|||||||
|
|
||||||
var cmdRemoveRules = &base.Command{
|
var cmdRemoveRules = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api rmrules [--server=127.0.0.1:8080] ruleTag1 ruleTag2...",
|
UsageLine: "{{.Exec}} api rmrules [--server=127.0.0.1:8080] [ruleTag]...",
|
||||||
Short: "Remove routing rules by ruleTag",
|
Short: "Remove routing rules by ruleTag",
|
||||||
Long: `
|
Long: `
|
||||||
Remove routing rules by ruleTag from Xray.
|
Remove routing rules by ruleTag from Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 ruleTag1 ruleTag2
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 ruleTag1 ruleTag2
|
||||||
`,
|
`,
|
||||||
Run: executeRemoveRules,
|
Run: executeRemoveRules,
|
||||||
|
@@ -14,25 +14,34 @@ import (
|
|||||||
var cmdSourceIpBlock = &base.Command{
|
var cmdSourceIpBlock = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api sib [--server=127.0.0.1:8080] -outbound=blocked -inbound=socks 1.2.3.4",
|
UsageLine: "{{.Exec}} api sib [--server=127.0.0.1:8080] -outbound=blocked -inbound=socks 1.2.3.4",
|
||||||
Short: "Drop connections by source ip",
|
Short: "Block connections by source IP",
|
||||||
Long: `
|
Long: `
|
||||||
Drop connections by source ip.
|
Block connections by source IP address.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
-outbound
|
-outbound
|
||||||
route traffic to specific outbound.
|
Specifies the outbound tag.
|
||||||
|
|
||||||
-inbound
|
-inbound
|
||||||
target traffig from specific inbound.
|
Specifies the inbound tag.
|
||||||
|
|
||||||
-ruletag
|
-ruletag
|
||||||
set ruleTag. Default sourceIpBlock
|
The ruleTag. Default sourceIpBlock
|
||||||
|
|
||||||
-reset
|
-reset
|
||||||
remove ruletag and apply new source IPs. Default false
|
remove ruletag and apply new source IPs. Default false
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -outbound=blocked -inbound=socks 1.2.3.4
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -outbound=blocked -inbound=socks 1.2.3.4 -reset
|
||||||
`,
|
`,
|
||||||
Run: executeSourceIpBlock,
|
Run: executeSourceIpBlock,
|
||||||
}
|
}
|
||||||
|
@@ -8,19 +8,26 @@ import (
|
|||||||
var cmdGetStats = &base.Command{
|
var cmdGetStats = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api stats [--server=127.0.0.1:8080] [-name '']",
|
UsageLine: "{{.Exec}} api stats [--server=127.0.0.1:8080] [-name '']",
|
||||||
Short: "Get statistics",
|
Short: "Retrieve statistics",
|
||||||
Long: `
|
Long: `
|
||||||
Get statistics from Xray.
|
Retrieve the statistics from Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
-name
|
-name
|
||||||
Name of the stat counter.
|
Name of the counter.
|
||||||
|
|
||||||
-reset
|
-reset
|
||||||
Reset the counter to fetching its value.
|
Reset the counter after fetching their values. Default false
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -name "inbound>>>statin>>>traffic>>>downlink"
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -name "inbound>>>statin>>>traffic>>>downlink"
|
||||||
`,
|
`,
|
||||||
Run: executeGetStats,
|
Run: executeGetStats,
|
||||||
|
@@ -7,21 +7,25 @@ import (
|
|||||||
|
|
||||||
var cmdOnlineStats = &base.Command{
|
var cmdOnlineStats = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api statsonline [--server=127.0.0.1:8080] [-name '']",
|
UsageLine: "{{.Exec}} api statsonline [--server=127.0.0.1:8080] [-email '']",
|
||||||
Short: "Get online user",
|
Short: "Retrieve the online session count for a user",
|
||||||
Long: `
|
Long: `
|
||||||
Get statistics from Xray.
|
Retrieve the current number of active sessions for a user from Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
-email
|
-email
|
||||||
email of the user.
|
The user's email address.
|
||||||
-reset
|
|
||||||
Reset the counter to fetching its value.
|
|
||||||
Example:
|
Example:
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -email "user1@test.com"
|
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -email "xray@love.com"
|
||||||
`,
|
`,
|
||||||
Run: executeOnlineStats,
|
Run: executeOnlineStats,
|
||||||
}
|
}
|
||||||
|
51
main/commands/all/api/stats_online_ip_list.go
Normal file
51
main/commands/all/api/stats_online_ip_list.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
statsService "github.com/xtls/xray-core/app/stats/command"
|
||||||
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdOnlineStatsIpList = &base.Command{
|
||||||
|
CustomFlags: true,
|
||||||
|
UsageLine: "{{.Exec}} api statsonlineiplist [--server=127.0.0.1:8080] [-email '']",
|
||||||
|
Short: "Retrieve a user's online IP addresses and access times",
|
||||||
|
Long: `
|
||||||
|
Retrieve the online IP addresses and corresponding access timestamps for a user from Xray.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
-s, -server <server:port>
|
||||||
|
The API server address. Default 127.0.0.1:8080
|
||||||
|
|
||||||
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
|
-email
|
||||||
|
The user's email address.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -email "xray@love.com"
|
||||||
|
`,
|
||||||
|
Run: executeOnlineStatsIpList,
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeOnlineStatsIpList(cmd *base.Command, args []string) {
|
||||||
|
setSharedFlags(cmd)
|
||||||
|
email := cmd.Flag.String("email", "", "")
|
||||||
|
cmd.Flag.Parse(args)
|
||||||
|
statName := "user>>>" + *email + ">>>online"
|
||||||
|
conn, ctx, close := dialAPIServer()
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
client := statsService.NewStatsServiceClient(conn)
|
||||||
|
r := &statsService.GetStatsRequest{
|
||||||
|
Name: statName,
|
||||||
|
Reset_: false,
|
||||||
|
}
|
||||||
|
resp, err := client.GetStatsOnlineIpList(ctx, r)
|
||||||
|
if err != nil {
|
||||||
|
base.Fatalf("failed to get stats: %s", err)
|
||||||
|
}
|
||||||
|
showJSONResponse(resp)
|
||||||
|
}
|
@@ -11,16 +11,23 @@ var cmdQueryStats = &base.Command{
|
|||||||
Short: "Query statistics",
|
Short: "Query statistics",
|
||||||
Long: `
|
Long: `
|
||||||
Query statistics from Xray.
|
Query statistics from Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
-pattern
|
-pattern
|
||||||
Pattern of the query.
|
Filter pattern for the statistics query.
|
||||||
|
|
||||||
-reset
|
-reset
|
||||||
Reset the counter to fetching its value.
|
Reset the counter after fetching their values. Default false
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -pattern "counter_"
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -pattern "counter_"
|
||||||
`,
|
`,
|
||||||
Run: executeQueryStats,
|
Run: executeQueryStats,
|
||||||
|
@@ -8,14 +8,21 @@ import (
|
|||||||
var cmdSysStats = &base.Command{
|
var cmdSysStats = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api statssys [--server=127.0.0.1:8080]",
|
UsageLine: "{{.Exec}} api statssys [--server=127.0.0.1:8080]",
|
||||||
Short: "Get system statistics",
|
Short: "Retrieve system statistics",
|
||||||
Long: `
|
Long: `
|
||||||
Get system statistics from Xray.
|
Retrieve system statistics from Xray.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
-s, -server
|
|
||||||
|
-s, -server <server:port>
|
||||||
The API server address. Default 127.0.0.1:8080
|
The API server address. Default 127.0.0.1:8080
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
-t, -timeout <seconds>
|
||||||
|
Timeout in seconds for calling API. Default 3
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080
|
||||||
`,
|
`,
|
||||||
Run: executeSysStats,
|
Run: executeSysStats,
|
||||||
}
|
}
|
||||||
|
@@ -50,17 +50,17 @@ func executeTypedMessageToJson(cmd *base.Command, args []string) {
|
|||||||
|
|
||||||
reader, err := confloader.LoadConfig(cmd.Flag.Arg(0))
|
reader, err := confloader.LoadConfig(cmd.Flag.Arg(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
base.Fatalf(err.Error())
|
base.Fatalf("failed to load config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(reader)
|
b, err := io.ReadAll(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
base.Fatalf(err.Error())
|
base.Fatalf("failed to read config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tm := cserial.TypedMessage{}
|
tm := cserial.TypedMessage{}
|
||||||
if err = json.Unmarshal(b, &tm); err != nil {
|
if err = json.Unmarshal(b, &tm); err != nil {
|
||||||
base.Fatalf(err.Error())
|
base.Fatalf("failed to unmarshal config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if j, ok := creflect.MarshalToJson(&tm, injectTypeInfo); ok {
|
if j, ok := creflect.MarshalToJson(&tm, injectTypeInfo); ok {
|
||||||
|
@@ -53,12 +53,12 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(unnamedArgs) < 1 {
|
if len(unnamedArgs) < 1 {
|
||||||
base.Fatalf("empty config list")
|
base.Fatalf("invalid config list length: %d", len(unnamedArgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
pbConfig, err := core.LoadConfig("auto", unnamedArgs)
|
pbConfig, err := core.LoadConfig("auto", unnamedArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
base.Fatalf(err.Error())
|
base.Fatalf("failed to load config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if optDump {
|
if optDump {
|
||||||
|
@@ -42,7 +42,8 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
|||||||
// Modify random bytes using algorithm described at:
|
// Modify random bytes using algorithm described at:
|
||||||
// https://cr.yp.to/ecdh.html.
|
// https://cr.yp.to/ecdh.html.
|
||||||
privateKey[0] &= 248
|
privateKey[0] &= 248
|
||||||
privateKey[31] &= 127 | 64
|
privateKey[31] &= 127
|
||||||
|
privateKey[31] |= 64
|
||||||
|
|
||||||
if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {
|
if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {
|
||||||
output = err.Error()
|
output = err.Error()
|
||||||
|
@@ -120,9 +120,9 @@ func writeFile(content []byte, name string) error {
|
|||||||
func printFile(certificate *cert.Certificate, name string) error {
|
func printFile(certificate *cert.Certificate, name string) error {
|
||||||
certPEM, keyPEM := certificate.ToPEM()
|
certPEM, keyPEM := certificate.ToPEM()
|
||||||
return task.Run(context.Background(), func() error {
|
return task.Run(context.Background(), func() error {
|
||||||
return writeFile(certPEM, name+"_cert.pem")
|
return writeFile(certPEM, name+".crt")
|
||||||
}, func() error {
|
}, func() error {
|
||||||
return writeFile(keyPEM, name+"_key.pem")
|
return writeFile(keyPEM, name+".key")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -45,6 +45,7 @@ The -dump flag tells Xray to print the merged config.
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmdRun.Run = executeRun // break init loop
|
cmdRun.Run = executeRun // break init loop
|
||||||
|
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/features/policy"
|
"github.com/xtls/xray-core/features/policy"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -63,10 +64,6 @@ func (d *DokodemoDoor) policy() policy.Session {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
type hasHandshakeAddressContext interface {
|
|
||||||
HandshakeAddressContext(ctx context.Context) net.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process implements proxy.Inbound.
|
// Process implements proxy.Inbound.
|
||||||
func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
errors.LogDebug(ctx, "processing connection from: ", conn.RemoteAddr())
|
errors.LogDebug(ctx, "processing connection from: ", conn.RemoteAddr())
|
||||||
@@ -86,11 +83,14 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
|||||||
destinationOverridden = true
|
destinationOverridden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if handshake, ok := conn.(hasHandshakeAddressContext); ok && !destinationOverridden {
|
if tlsConn, ok := conn.(tls.Interface); ok && !destinationOverridden {
|
||||||
addr := handshake.HandshakeAddressContext(ctx)
|
if serverName := tlsConn.HandshakeContextServerName(ctx); serverName != "" {
|
||||||
if addr != nil {
|
dest.Address = net.DomainAddress(serverName)
|
||||||
dest.Address = addr
|
|
||||||
destinationOverridden = true
|
destinationOverridden = true
|
||||||
|
ctx = session.ContextWithMitmServerName(ctx, serverName)
|
||||||
|
}
|
||||||
|
if tlsConn.NegotiatedProtocol() != "h2" {
|
||||||
|
ctx = session.ContextWithMitmAlpn11(ctx, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,12 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"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/crypto"
|
||||||
"github.com/xtls/xray-core/common/dice"
|
"github.com/xtls/xray-core/common/dice"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -414,7 +414,7 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
noise = n.Packet
|
noise = n.Packet
|
||||||
} else {
|
} else {
|
||||||
//Random noise
|
//Random noise
|
||||||
noise, err = GenerateRandomBytes(randBetween(int64(n.LengthMin),
|
noise, err = GenerateRandomBytes(crypto.RandBetween(int64(n.LengthMin),
|
||||||
int64(n.LengthMax)))
|
int64(n.LengthMax)))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -423,7 +423,7 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)})
|
w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)})
|
||||||
|
|
||||||
if n.DelayMin != 0 || n.DelayMax != 0 {
|
if n.DelayMin != 0 || n.DelayMax != 0 {
|
||||||
time.Sleep(time.Duration(randBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond)
|
time.Sleep(time.Duration(crypto.RandBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +452,7 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
|||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
var hello []byte
|
var hello []byte
|
||||||
for from := 0; ; {
|
for from := 0; ; {
|
||||||
to := from + int(randBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
||||||
if to > len(data) {
|
if to > len(data) {
|
||||||
to = len(data)
|
to = len(data)
|
||||||
}
|
}
|
||||||
@@ -466,7 +466,7 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
|||||||
hello = append(hello, buf[:5+l]...)
|
hello = append(hello, buf[:5+l]...)
|
||||||
} else {
|
} else {
|
||||||
_, err := f.writer.Write(buf[:5+l])
|
_, err := f.writer.Write(buf[:5+l])
|
||||||
time.Sleep(time.Duration(randBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -493,13 +493,13 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
|||||||
return f.writer.Write(b)
|
return f.writer.Write(b)
|
||||||
}
|
}
|
||||||
for from := 0; ; {
|
for from := 0; ; {
|
||||||
to := from + int(randBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
||||||
if to > len(b) {
|
if to > len(b) {
|
||||||
to = len(b)
|
to = len(b)
|
||||||
}
|
}
|
||||||
n, err := f.writer.Write(b[from:to])
|
n, err := f.writer.Write(b[from:to])
|
||||||
from += n
|
from += n
|
||||||
time.Sleep(time.Duration(randBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return from, err
|
return from, err
|
||||||
}
|
}
|
||||||
@@ -509,14 +509,6 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stolen from github.com/xtls/xray-core/transport/internet/reality
|
|
||||||
func randBetween(left int64, right int64) int64 {
|
|
||||||
if left == right {
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left))
|
|
||||||
return left + bigInt.Int64()
|
|
||||||
}
|
|
||||||
func GenerateRandomBytes(n int64) ([]byte, error) {
|
func GenerateRandomBytes(n int64) ([]byte, error) {
|
||||||
b := make([]byte, n)
|
b := make([]byte, n)
|
||||||
_, err := rand.Read(b)
|
_, err := rand.Read(b)
|
||||||
|
@@ -151,6 +151,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
|||||||
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
responseFunc := func() error {
|
responseFunc := func() error {
|
||||||
|
ob.CanSpliceCopy = 1
|
||||||
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||||
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
|
@@ -207,6 +207,7 @@ func (s *Server) handleConnect(ctx context.Context, _ *http.Request, reader *buf
|
|||||||
}
|
}
|
||||||
|
|
||||||
responseDone := func() error {
|
responseDone := func() error {
|
||||||
|
inbound.CanSpliceCopy = 1
|
||||||
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
||||||
|
|
||||||
v2writer := buf.NewWriter(conn)
|
v2writer := buf.NewWriter(conn)
|
||||||
|
162
proxy/proxy.go
162
proxy/proxy.go
@@ -107,19 +107,33 @@ type TrafficState struct {
|
|||||||
IsTLS bool
|
IsTLS bool
|
||||||
Cipher uint16
|
Cipher uint16
|
||||||
RemainingServerHello int32
|
RemainingServerHello int32
|
||||||
|
Inbound InboundState
|
||||||
|
Outbound OutboundState
|
||||||
|
}
|
||||||
|
|
||||||
|
type InboundState struct {
|
||||||
// reader link state
|
// reader link state
|
||||||
WithinPaddingBuffers bool
|
WithinPaddingBuffers bool
|
||||||
DownlinkReaderDirectCopy bool
|
|
||||||
UplinkReaderDirectCopy bool
|
UplinkReaderDirectCopy bool
|
||||||
RemainingCommand int32
|
RemainingCommand int32
|
||||||
RemainingContent int32
|
RemainingContent int32
|
||||||
RemainingPadding int32
|
RemainingPadding int32
|
||||||
CurrentCommand int
|
CurrentCommand int
|
||||||
|
|
||||||
// write link state
|
// write link state
|
||||||
IsPadding bool
|
IsPadding bool
|
||||||
DownlinkWriterDirectCopy bool
|
DownlinkWriterDirectCopy bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutboundState struct {
|
||||||
|
// reader link state
|
||||||
|
WithinPaddingBuffers bool
|
||||||
|
DownlinkReaderDirectCopy bool
|
||||||
|
RemainingCommand int32
|
||||||
|
RemainingContent int32
|
||||||
|
RemainingPadding int32
|
||||||
|
CurrentCommand int
|
||||||
|
// write link state
|
||||||
|
IsPadding bool
|
||||||
UplinkWriterDirectCopy bool
|
UplinkWriterDirectCopy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,8 +146,8 @@ func NewTrafficState(userUUID []byte) *TrafficState {
|
|||||||
IsTLS: false,
|
IsTLS: false,
|
||||||
Cipher: 0,
|
Cipher: 0,
|
||||||
RemainingServerHello: -1,
|
RemainingServerHello: -1,
|
||||||
|
Inbound: InboundState{
|
||||||
WithinPaddingBuffers: true,
|
WithinPaddingBuffers: true,
|
||||||
DownlinkReaderDirectCopy: false,
|
|
||||||
UplinkReaderDirectCopy: false,
|
UplinkReaderDirectCopy: false,
|
||||||
RemainingCommand: -1,
|
RemainingCommand: -1,
|
||||||
RemainingContent: -1,
|
RemainingContent: -1,
|
||||||
@@ -141,7 +155,17 @@ func NewTrafficState(userUUID []byte) *TrafficState {
|
|||||||
CurrentCommand: 0,
|
CurrentCommand: 0,
|
||||||
IsPadding: true,
|
IsPadding: true,
|
||||||
DownlinkWriterDirectCopy: false,
|
DownlinkWriterDirectCopy: false,
|
||||||
|
},
|
||||||
|
Outbound: OutboundState{
|
||||||
|
WithinPaddingBuffers: true,
|
||||||
|
DownlinkReaderDirectCopy: false,
|
||||||
|
RemainingCommand: -1,
|
||||||
|
RemainingContent: -1,
|
||||||
|
RemainingPadding: -1,
|
||||||
|
CurrentCommand: 0,
|
||||||
|
IsPadding: true,
|
||||||
UplinkWriterDirectCopy: false,
|
UplinkWriterDirectCopy: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,28 +190,43 @@ func NewVisionReader(reader buf.Reader, state *TrafficState, isUplink bool, cont
|
|||||||
func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
buffer, err := w.Reader.ReadMultiBuffer()
|
buffer, err := w.Reader.ReadMultiBuffer()
|
||||||
if !buffer.IsEmpty() {
|
if !buffer.IsEmpty() {
|
||||||
if w.trafficState.WithinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 {
|
var withinPaddingBuffers *bool
|
||||||
|
var remainingContent *int32
|
||||||
|
var remainingPadding *int32
|
||||||
|
var currentCommand *int
|
||||||
|
var switchToDirectCopy *bool
|
||||||
|
if w.isUplink {
|
||||||
|
withinPaddingBuffers = &w.trafficState.Inbound.WithinPaddingBuffers
|
||||||
|
remainingContent = &w.trafficState.Inbound.RemainingContent
|
||||||
|
remainingPadding = &w.trafficState.Inbound.RemainingPadding
|
||||||
|
currentCommand = &w.trafficState.Inbound.CurrentCommand
|
||||||
|
switchToDirectCopy = &w.trafficState.Inbound.UplinkReaderDirectCopy
|
||||||
|
} else {
|
||||||
|
withinPaddingBuffers = &w.trafficState.Outbound.WithinPaddingBuffers
|
||||||
|
remainingContent = &w.trafficState.Outbound.RemainingContent
|
||||||
|
remainingPadding = &w.trafficState.Outbound.RemainingPadding
|
||||||
|
currentCommand = &w.trafficState.Outbound.CurrentCommand
|
||||||
|
switchToDirectCopy = &w.trafficState.Outbound.DownlinkReaderDirectCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
if *withinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 {
|
||||||
mb2 := make(buf.MultiBuffer, 0, len(buffer))
|
mb2 := make(buf.MultiBuffer, 0, len(buffer))
|
||||||
for _, b := range buffer {
|
for _, b := range buffer {
|
||||||
newbuffer := XtlsUnpadding(b, w.trafficState, w.ctx)
|
newbuffer := XtlsUnpadding(b, w.trafficState, w.isUplink, w.ctx)
|
||||||
if newbuffer.Len() > 0 {
|
if newbuffer.Len() > 0 {
|
||||||
mb2 = append(mb2, newbuffer)
|
mb2 = append(mb2, newbuffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer = mb2
|
buffer = mb2
|
||||||
if w.trafficState.RemainingContent > 0 || w.trafficState.RemainingPadding > 0 || w.trafficState.CurrentCommand == 0 {
|
if *remainingContent > 0 || *remainingPadding > 0 || *currentCommand == 0 {
|
||||||
w.trafficState.WithinPaddingBuffers = true
|
*withinPaddingBuffers = true
|
||||||
} else if w.trafficState.CurrentCommand == 1 {
|
} else if *currentCommand == 1 {
|
||||||
w.trafficState.WithinPaddingBuffers = false
|
*withinPaddingBuffers = false
|
||||||
} else if w.trafficState.CurrentCommand == 2 {
|
} else if *currentCommand == 2 {
|
||||||
w.trafficState.WithinPaddingBuffers = false
|
*withinPaddingBuffers = false
|
||||||
if w.isUplink {
|
*switchToDirectCopy = true
|
||||||
w.trafficState.UplinkReaderDirectCopy = true
|
|
||||||
} else {
|
} else {
|
||||||
w.trafficState.DownlinkReaderDirectCopy = true
|
errors.LogInfo(w.ctx, "XtlsRead unknown command ", *currentCommand, buffer.Len())
|
||||||
}
|
|
||||||
} else {
|
|
||||||
errors.LogInfo(w.ctx, "XtlsRead unknown command ", w.trafficState.CurrentCommand, buffer.Len())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if w.trafficState.NumberOfPacketToFilter > 0 {
|
if w.trafficState.NumberOfPacketToFilter > 0 {
|
||||||
@@ -223,7 +262,16 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
if w.trafficState.NumberOfPacketToFilter > 0 {
|
if w.trafficState.NumberOfPacketToFilter > 0 {
|
||||||
XtlsFilterTls(mb, w.trafficState, w.ctx)
|
XtlsFilterTls(mb, w.trafficState, w.ctx)
|
||||||
}
|
}
|
||||||
if w.trafficState.IsPadding {
|
var isPadding *bool
|
||||||
|
var switchToDirectCopy *bool
|
||||||
|
if w.isUplink {
|
||||||
|
isPadding = &w.trafficState.Outbound.IsPadding
|
||||||
|
switchToDirectCopy = &w.trafficState.Outbound.UplinkWriterDirectCopy
|
||||||
|
} else {
|
||||||
|
isPadding = &w.trafficState.Inbound.IsPadding
|
||||||
|
switchToDirectCopy = &w.trafficState.Inbound.DownlinkWriterDirectCopy
|
||||||
|
}
|
||||||
|
if *isPadding {
|
||||||
if len(mb) == 1 && mb[0] == nil {
|
if len(mb) == 1 && mb[0] == nil {
|
||||||
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
|
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
|
||||||
return w.Writer.WriteMultiBuffer(mb)
|
return w.Writer.WriteMultiBuffer(mb)
|
||||||
@@ -233,11 +281,7 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
for i, b := range mb {
|
for i, b := range mb {
|
||||||
if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) {
|
if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) {
|
||||||
if w.trafficState.EnableXtls {
|
if w.trafficState.EnableXtls {
|
||||||
if w.isUplink {
|
*switchToDirectCopy = true
|
||||||
w.trafficState.UplinkWriterDirectCopy = true
|
|
||||||
} else {
|
|
||||||
w.trafficState.DownlinkWriterDirectCopy = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var command byte = CommandPaddingContinue
|
var command byte = CommandPaddingContinue
|
||||||
if i == len(mb)-1 {
|
if i == len(mb)-1 {
|
||||||
@@ -247,16 +291,16 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx)
|
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx)
|
||||||
w.trafficState.IsPadding = false // padding going to end
|
*isPadding = false // padding going to end
|
||||||
longPadding = false
|
longPadding = false
|
||||||
continue
|
continue
|
||||||
} else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
|
} else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
|
||||||
w.trafficState.IsPadding = false
|
*isPadding = false
|
||||||
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx)
|
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
var command byte = CommandPaddingContinue
|
var command byte = CommandPaddingContinue
|
||||||
if i == len(mb)-1 && !w.trafficState.IsPadding {
|
if i == len(mb)-1 && !*isPadding {
|
||||||
command = CommandPaddingEnd
|
command = CommandPaddingEnd
|
||||||
if w.trafficState.EnableXtls {
|
if w.trafficState.EnableXtls {
|
||||||
command = CommandPaddingDirect
|
command = CommandPaddingDirect
|
||||||
@@ -343,38 +387,53 @@ func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XtlsUnpadding remove padding and parse command
|
// XtlsUnpadding remove padding and parse command
|
||||||
func XtlsUnpadding(b *buf.Buffer, s *TrafficState, ctx context.Context) *buf.Buffer {
|
func XtlsUnpadding(b *buf.Buffer, s *TrafficState, isUplink bool, ctx context.Context) *buf.Buffer {
|
||||||
if s.RemainingCommand == -1 && s.RemainingContent == -1 && s.RemainingPadding == -1 { // initial state
|
var remainingCommand *int32
|
||||||
|
var remainingContent *int32
|
||||||
|
var remainingPadding *int32
|
||||||
|
var currentCommand *int
|
||||||
|
if isUplink {
|
||||||
|
remainingCommand = &s.Inbound.RemainingCommand
|
||||||
|
remainingContent = &s.Inbound.RemainingContent
|
||||||
|
remainingPadding = &s.Inbound.RemainingPadding
|
||||||
|
currentCommand = &s.Inbound.CurrentCommand
|
||||||
|
} else {
|
||||||
|
remainingCommand = &s.Outbound.RemainingCommand
|
||||||
|
remainingContent = &s.Outbound.RemainingContent
|
||||||
|
remainingPadding = &s.Outbound.RemainingPadding
|
||||||
|
currentCommand = &s.Outbound.CurrentCommand
|
||||||
|
}
|
||||||
|
if *remainingCommand == -1 && *remainingContent == -1 && *remainingPadding == -1 { // initial state
|
||||||
if b.Len() >= 21 && bytes.Equal(s.UserUUID, b.BytesTo(16)) {
|
if b.Len() >= 21 && bytes.Equal(s.UserUUID, b.BytesTo(16)) {
|
||||||
b.Advance(16)
|
b.Advance(16)
|
||||||
s.RemainingCommand = 5
|
*remainingCommand = 5
|
||||||
} else {
|
} else {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newbuffer := buf.New()
|
newbuffer := buf.New()
|
||||||
for b.Len() > 0 {
|
for b.Len() > 0 {
|
||||||
if s.RemainingCommand > 0 {
|
if *remainingCommand > 0 {
|
||||||
data, err := b.ReadByte()
|
data, err := b.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newbuffer
|
return newbuffer
|
||||||
}
|
}
|
||||||
switch s.RemainingCommand {
|
switch *remainingCommand {
|
||||||
case 5:
|
case 5:
|
||||||
s.CurrentCommand = int(data)
|
*currentCommand = int(data)
|
||||||
case 4:
|
case 4:
|
||||||
s.RemainingContent = int32(data) << 8
|
*remainingContent = int32(data) << 8
|
||||||
case 3:
|
case 3:
|
||||||
s.RemainingContent = s.RemainingContent | int32(data)
|
*remainingContent = *remainingContent | int32(data)
|
||||||
case 2:
|
case 2:
|
||||||
s.RemainingPadding = int32(data) << 8
|
*remainingPadding = int32(data) << 8
|
||||||
case 1:
|
case 1:
|
||||||
s.RemainingPadding = s.RemainingPadding | int32(data)
|
*remainingPadding = *remainingPadding | int32(data)
|
||||||
errors.LogInfo(ctx, "Xtls Unpadding new block, content ", s.RemainingContent, " padding ", s.RemainingPadding, " command ", s.CurrentCommand)
|
errors.LogInfo(ctx, "Xtls Unpadding new block, content ", *remainingContent, " padding ", *remainingPadding, " command ", *currentCommand)
|
||||||
}
|
}
|
||||||
s.RemainingCommand--
|
*remainingCommand--
|
||||||
} else if s.RemainingContent > 0 {
|
} else if *remainingContent > 0 {
|
||||||
len := s.RemainingContent
|
len := *remainingContent
|
||||||
if b.Len() < len {
|
if b.Len() < len {
|
||||||
len = b.Len()
|
len = b.Len()
|
||||||
}
|
}
|
||||||
@@ -383,22 +442,22 @@ func XtlsUnpadding(b *buf.Buffer, s *TrafficState, ctx context.Context) *buf.Buf
|
|||||||
return newbuffer
|
return newbuffer
|
||||||
}
|
}
|
||||||
newbuffer.Write(data)
|
newbuffer.Write(data)
|
||||||
s.RemainingContent -= len
|
*remainingContent -= len
|
||||||
} else { // remainingPadding > 0
|
} else { // remainingPadding > 0
|
||||||
len := s.RemainingPadding
|
len := *remainingPadding
|
||||||
if b.Len() < len {
|
if b.Len() < len {
|
||||||
len = b.Len()
|
len = b.Len()
|
||||||
}
|
}
|
||||||
b.Advance(len)
|
b.Advance(len)
|
||||||
s.RemainingPadding -= len
|
*remainingPadding -= len
|
||||||
}
|
}
|
||||||
if s.RemainingCommand <= 0 && s.RemainingContent <= 0 && s.RemainingPadding <= 0 { // this block done
|
if *remainingCommand <= 0 && *remainingContent <= 0 && *remainingPadding <= 0 { // this block done
|
||||||
if s.CurrentCommand == 0 {
|
if *currentCommand == 0 {
|
||||||
s.RemainingCommand = 5
|
*remainingCommand = 5
|
||||||
} else {
|
} else {
|
||||||
s.RemainingCommand = -1 // set to initial state
|
*remainingCommand = -1 // set to initial state
|
||||||
s.RemainingContent = -1
|
*remainingContent = -1
|
||||||
s.RemainingPadding = -1
|
*remainingPadding = -1
|
||||||
if b.Len() > 0 { // shouldn't happen
|
if b.Len() > 0 { // shouldn't happen
|
||||||
newbuffer.Write(b.Bytes())
|
newbuffer.Write(b.Bytes())
|
||||||
}
|
}
|
||||||
@@ -465,7 +524,7 @@ func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnwrapRawConn support unwrap stats, tls, utls, reality and proxyproto conn and get raw tcp conn from it
|
// UnwrapRawConn support unwrap stats, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it
|
||||||
func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
||||||
var readCounter, writerCounter stats.Counter
|
var readCounter, writerCounter stats.Counter
|
||||||
if conn != nil {
|
if conn != nil {
|
||||||
@@ -488,6 +547,9 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
|||||||
conn = pc.Raw()
|
conn = pc.Raw()
|
||||||
// 8192 > 4096, there is no need to process pc's bufReader
|
// 8192 > 4096, there is no need to process pc's bufReader
|
||||||
}
|
}
|
||||||
|
if uc, ok := conn.(*internet.UnixConnWrapper); ok {
|
||||||
|
conn = uc.UnixConn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return conn, readCounter, writerCounter
|
return conn, readCounter, writerCounter
|
||||||
}
|
}
|
||||||
|
@@ -146,6 +146,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
|||||||
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
responseFunc = func() error {
|
responseFunc = func() error {
|
||||||
|
ob.CanSpliceCopy = 1
|
||||||
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||||
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
@@ -161,6 +162,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
|||||||
return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer))
|
return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
responseFunc = func() error {
|
responseFunc = func() error {
|
||||||
|
ob.CanSpliceCopy = 1
|
||||||
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||||
reader := &UDPReader{Reader: udpConn}
|
reader := &UDPReader{Reader: udpConn}
|
||||||
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
||||||
|
@@ -199,6 +199,7 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
|
|||||||
}
|
}
|
||||||
|
|
||||||
responseDone := func() error {
|
responseDone := func() error {
|
||||||
|
inbound.CanSpliceCopy = 1
|
||||||
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
||||||
|
|
||||||
v2writer := buf.NewWriter(writer)
|
v2writer := buf.NewWriter(writer)
|
||||||
@@ -256,6 +257,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
|
|||||||
if inbound != nil && inbound.Source.IsValid() {
|
if inbound != nil && inbound.Source.IsValid() {
|
||||||
errors.LogInfo(ctx, "client UDP connection from ", inbound.Source)
|
errors.LogInfo(ctx, "client UDP connection from ", inbound.Source)
|
||||||
}
|
}
|
||||||
|
inbound.CanSpliceCopy = 1
|
||||||
|
|
||||||
var dest *net.Destination
|
var dest *net.Destination
|
||||||
|
|
||||||
|
@@ -175,16 +175,16 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
|
|||||||
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
||||||
err := func() error {
|
err := func() error {
|
||||||
for {
|
for {
|
||||||
if isUplink && trafficState.UplinkReaderDirectCopy || !isUplink && trafficState.DownlinkReaderDirectCopy {
|
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||||
var writerConn net.Conn
|
var writerConn net.Conn
|
||||||
var inTimer *signal.ActivityTimer
|
var inTimer *signal.ActivityTimer
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
|
||||||
writerConn = inbound.Conn
|
writerConn = inbound.Conn
|
||||||
inTimer = inbound.Timer
|
inTimer = inbound.Timer
|
||||||
if inbound.CanSpliceCopy == 2 {
|
if isUplink && inbound.CanSpliceCopy == 2 {
|
||||||
inbound.CanSpliceCopy = 1
|
inbound.CanSpliceCopy = 1
|
||||||
}
|
}
|
||||||
if ob != nil && ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can change
|
if !isUplink && ob != nil && ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can change
|
||||||
ob.CanSpliceCopy = 1
|
ob.CanSpliceCopy = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer,
|
|||||||
buffer, err := reader.ReadMultiBuffer()
|
buffer, err := reader.ReadMultiBuffer()
|
||||||
if !buffer.IsEmpty() {
|
if !buffer.IsEmpty() {
|
||||||
timer.Update()
|
timer.Update()
|
||||||
if isUplink && trafficState.UplinkReaderDirectCopy || !isUplink && trafficState.DownlinkReaderDirectCopy {
|
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||||
// XTLS Vision processes struct TLS Conn's input and rawInput
|
// XTLS Vision processes struct TLS Conn's input and rawInput
|
||||||
if inputBuffer, err := buf.ReadFrom(input); err == nil {
|
if inputBuffer, err := buf.ReadFrom(input); err == nil {
|
||||||
if !inputBuffer.IsEmpty() {
|
if !inputBuffer.IsEmpty() {
|
||||||
@@ -227,12 +227,12 @@ func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdate
|
|||||||
var ct stats.Counter
|
var ct stats.Counter
|
||||||
for {
|
for {
|
||||||
buffer, err := reader.ReadMultiBuffer()
|
buffer, err := reader.ReadMultiBuffer()
|
||||||
if isUplink && trafficState.UplinkWriterDirectCopy || !isUplink && trafficState.DownlinkWriterDirectCopy {
|
if isUplink && trafficState.Outbound.UplinkWriterDirectCopy || !isUplink && trafficState.Inbound.DownlinkWriterDirectCopy {
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
if inbound.CanSpliceCopy == 2 {
|
if !isUplink && inbound.CanSpliceCopy == 2 {
|
||||||
inbound.CanSpliceCopy = 1
|
inbound.CanSpliceCopy = 1
|
||||||
}
|
}
|
||||||
if ob != nil && ob.CanSpliceCopy == 2 {
|
if isUplink && ob != nil && ob.CanSpliceCopy == 2 {
|
||||||
ob.CanSpliceCopy = 1
|
ob.CanSpliceCopy = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,9 +240,9 @@ func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdate
|
|||||||
writer = buf.NewWriter(rawConn)
|
writer = buf.NewWriter(rawConn)
|
||||||
ct = writerCounter
|
ct = writerCounter
|
||||||
if isUplink {
|
if isUplink {
|
||||||
trafficState.UplinkWriterDirectCopy = false
|
trafficState.Outbound.UplinkWriterDirectCopy = false
|
||||||
} else {
|
} else {
|
||||||
trafficState.DownlinkWriterDirectCopy = false
|
trafficState.Inbound.DownlinkWriterDirectCopy = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !buffer.IsEmpty() {
|
if !buffer.IsEmpty() {
|
||||||
|
@@ -95,6 +95,67 @@ func (DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
|||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AddressPortStrategy int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
AddressPortStrategy_None AddressPortStrategy = 0
|
||||||
|
AddressPortStrategy_SrvPortOnly AddressPortStrategy = 1
|
||||||
|
AddressPortStrategy_SrvAddressOnly AddressPortStrategy = 2
|
||||||
|
AddressPortStrategy_SrvPortAndAddress AddressPortStrategy = 3
|
||||||
|
AddressPortStrategy_TxtPortOnly AddressPortStrategy = 4
|
||||||
|
AddressPortStrategy_TxtAddressOnly AddressPortStrategy = 5
|
||||||
|
AddressPortStrategy_TxtPortAndAddress AddressPortStrategy = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for AddressPortStrategy.
|
||||||
|
var (
|
||||||
|
AddressPortStrategy_name = map[int32]string{
|
||||||
|
0: "None",
|
||||||
|
1: "SrvPortOnly",
|
||||||
|
2: "SrvAddressOnly",
|
||||||
|
3: "SrvPortAndAddress",
|
||||||
|
4: "TxtPortOnly",
|
||||||
|
5: "TxtAddressOnly",
|
||||||
|
6: "TxtPortAndAddress",
|
||||||
|
}
|
||||||
|
AddressPortStrategy_value = map[string]int32{
|
||||||
|
"None": 0,
|
||||||
|
"SrvPortOnly": 1,
|
||||||
|
"SrvAddressOnly": 2,
|
||||||
|
"SrvPortAndAddress": 3,
|
||||||
|
"TxtPortOnly": 4,
|
||||||
|
"TxtAddressOnly": 5,
|
||||||
|
"TxtPortAndAddress": 6,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x AddressPortStrategy) Enum() *AddressPortStrategy {
|
||||||
|
p := new(AddressPortStrategy)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x AddressPortStrategy) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (AddressPortStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_transport_internet_config_proto_enumTypes[1].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (AddressPortStrategy) Type() protoreflect.EnumType {
|
||||||
|
return &file_transport_internet_config_proto_enumTypes[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x AddressPortStrategy) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use AddressPortStrategy.Descriptor instead.
|
||||||
|
func (AddressPortStrategy) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
type SocketConfig_TProxyMode int32
|
type SocketConfig_TProxyMode int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -131,11 +192,11 @@ func (x SocketConfig_TProxyMode) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
|
func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
|
||||||
return file_transport_internet_config_proto_enumTypes[1].Descriptor()
|
return file_transport_internet_config_proto_enumTypes[2].Descriptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
|
func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
|
||||||
return &file_transport_internet_config_proto_enumTypes[1]
|
return &file_transport_internet_config_proto_enumTypes[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
|
func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
|
||||||
@@ -451,6 +512,7 @@ type SocketConfig struct {
|
|||||||
Penetrate bool `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
|
Penetrate bool `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
|
||||||
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
||||||
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
|
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
|
||||||
|
AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SocketConfig) Reset() {
|
func (x *SocketConfig) Reset() {
|
||||||
@@ -623,6 +685,13 @@ func (x *SocketConfig) GetCustomSockopt() []*CustomSockopt {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SocketConfig) GetAddressPortStrategy() AddressPortStrategy {
|
||||||
|
if x != nil {
|
||||||
|
return x.AddressPortStrategy
|
||||||
|
}
|
||||||
|
return AddressPortStrategy_None
|
||||||
|
}
|
||||||
|
|
||||||
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_transport_internet_config_proto_rawDesc = []byte{
|
var file_transport_internet_config_proto_rawDesc = []byte{
|
||||||
@@ -678,7 +747,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
|
|||||||
0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
|
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
|
||||||
0x65, 0x22, 0x9b, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
0x65, 0x22, 0xfd, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
|
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
|
||||||
0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20,
|
0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20,
|
||||||
0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f,
|
0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f,
|
||||||
@@ -732,28 +801,44 @@ var file_transport_internet_config_proto_rawDesc = []byte{
|
|||||||
0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
|
0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
|
||||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
||||||
0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x52,
|
0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x52,
|
||||||
0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x22, 0x2f,
|
0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x12, 0x60,
|
||||||
0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03,
|
0x0a, 0x15, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73,
|
||||||
0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10,
|
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e,
|
||||||
0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
|
||||||
0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
|
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50,
|
||||||
0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a,
|
0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13, 0x61, 0x64, 0x64,
|
||||||
0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45,
|
0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||||
0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50,
|
0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07,
|
||||||
0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10,
|
0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78,
|
||||||
0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12,
|
0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10,
|
||||||
0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a,
|
0x02, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||||
0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09,
|
0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12,
|
||||||
0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46,
|
0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55,
|
||||||
0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46,
|
0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f,
|
||||||
0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x67, 0x0a, 0x1b, 0x63,
|
0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
|
||||||
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10,
|
||||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69,
|
0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12,
|
||||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
|
0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d,
|
||||||
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a,
|
||||||
0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61,
|
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a,
|
||||||
0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
|
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01,
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72,
|
||||||
|
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12,
|
||||||
|
0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01,
|
||||||
|
0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e,
|
||||||
|
0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41,
|
||||||
|
0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54,
|
||||||
|
0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e,
|
||||||
|
0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05,
|
||||||
|
0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64,
|
||||||
|
0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||||
|
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||||
|
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||||
|
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
|
||||||
|
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||||
|
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -768,33 +853,35 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_transport_internet_config_proto_rawDescData
|
return file_transport_internet_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||||
var file_transport_internet_config_proto_goTypes = []any{
|
var file_transport_internet_config_proto_goTypes = []any{
|
||||||
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
|
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
|
||||||
(SocketConfig_TProxyMode)(0), // 1: xray.transport.internet.SocketConfig.TProxyMode
|
(AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy
|
||||||
(*TransportConfig)(nil), // 2: xray.transport.internet.TransportConfig
|
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
||||||
(*StreamConfig)(nil), // 3: xray.transport.internet.StreamConfig
|
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
|
||||||
(*ProxyConfig)(nil), // 4: xray.transport.internet.ProxyConfig
|
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
||||||
(*CustomSockopt)(nil), // 5: xray.transport.internet.CustomSockopt
|
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
|
||||||
(*SocketConfig)(nil), // 6: xray.transport.internet.SocketConfig
|
(*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt
|
||||||
(*serial.TypedMessage)(nil), // 7: xray.common.serial.TypedMessage
|
(*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig
|
||||||
(*net.IPOrDomain)(nil), // 8: xray.common.net.IPOrDomain
|
(*serial.TypedMessage)(nil), // 8: xray.common.serial.TypedMessage
|
||||||
|
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain
|
||||||
}
|
}
|
||||||
var file_transport_internet_config_proto_depIdxs = []int32{
|
var file_transport_internet_config_proto_depIdxs = []int32{
|
||||||
7, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
8, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||||
8, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
9, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||||
2, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||||
7, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
8, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||||
6, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
7, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||||
1, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
2, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||||
0, // 6: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
0, // 6: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||||
5, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
6, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||||
8, // [8:8] is the sub-list for method output_type
|
1, // 8: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||||
8, // [8:8] is the sub-list for method input_type
|
9, // [9:9] is the sub-list for method output_type
|
||||||
8, // [8:8] is the sub-list for extension type_name
|
9, // [9:9] is the sub-list for method input_type
|
||||||
8, // [8:8] is the sub-list for extension extendee
|
9, // [9:9] is the sub-list for extension type_name
|
||||||
0, // [0:8] is the sub-list for field type_name
|
9, // [9:9] is the sub-list for extension extendee
|
||||||
|
0, // [0:9] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_transport_internet_config_proto_init() }
|
func init() { file_transport_internet_config_proto_init() }
|
||||||
@@ -807,7 +894,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_transport_internet_config_proto_rawDesc,
|
RawDescriptor: file_transport_internet_config_proto_rawDesc,
|
||||||
NumEnums: 2,
|
NumEnums: 3,
|
||||||
NumMessages: 5,
|
NumMessages: 5,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
|
@@ -23,6 +23,16 @@ enum DomainStrategy {
|
|||||||
FORCE_IP64 = 10;
|
FORCE_IP64 = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AddressPortStrategy {
|
||||||
|
None = 0;
|
||||||
|
SrvPortOnly = 1;
|
||||||
|
SrvAddressOnly = 2;
|
||||||
|
SrvPortAndAddress = 3;
|
||||||
|
TxtPortOnly = 4;
|
||||||
|
TxtAddressOnly = 5;
|
||||||
|
TxtPortAndAddress = 6;
|
||||||
|
}
|
||||||
|
|
||||||
message TransportConfig {
|
message TransportConfig {
|
||||||
// Transport protocol name.
|
// Transport protocol name.
|
||||||
string protocol_name = 3;
|
string protocol_name = 3;
|
||||||
@@ -116,4 +126,6 @@ message SocketConfig {
|
|||||||
bool tcp_mptcp = 19;
|
bool tcp_mptcp = 19;
|
||||||
|
|
||||||
repeated CustomSockopt customSockopt = 20;
|
repeated CustomSockopt customSockopt = 20;
|
||||||
|
|
||||||
|
AddressPortStrategy address_port_strategy = 21;
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,9 @@ package internet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
gonet "net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/dice"
|
"github.com/xtls/xray-core/common/dice"
|
||||||
@@ -140,6 +143,93 @@ func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkAddressPortStrategy(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (*net.Destination, error) {
|
||||||
|
if sockopt.AddressPortStrategy == AddressPortStrategy_None {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
newDest := dest
|
||||||
|
var OverridePort, OverrideAddress bool
|
||||||
|
var OverrideBy string
|
||||||
|
switch sockopt.AddressPortStrategy {
|
||||||
|
case AddressPortStrategy_SrvPortOnly:
|
||||||
|
OverridePort = true
|
||||||
|
OverrideAddress = false
|
||||||
|
OverrideBy = "srv"
|
||||||
|
case AddressPortStrategy_SrvAddressOnly:
|
||||||
|
OverridePort = false
|
||||||
|
OverrideAddress = true
|
||||||
|
OverrideBy = "srv"
|
||||||
|
case AddressPortStrategy_SrvPortAndAddress:
|
||||||
|
OverridePort = true
|
||||||
|
OverrideAddress = true
|
||||||
|
OverrideBy = "srv"
|
||||||
|
case AddressPortStrategy_TxtPortOnly:
|
||||||
|
OverridePort = true
|
||||||
|
OverrideAddress = false
|
||||||
|
OverrideBy = "txt"
|
||||||
|
case AddressPortStrategy_TxtAddressOnly:
|
||||||
|
OverridePort = false
|
||||||
|
OverrideAddress = true
|
||||||
|
OverrideBy = "txt"
|
||||||
|
case AddressPortStrategy_TxtPortAndAddress:
|
||||||
|
OverridePort = true
|
||||||
|
OverrideAddress = true
|
||||||
|
OverrideBy = "txt"
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown AddressPortStrategy")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dest.Address.Family().IsDomain() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if OverrideBy == "srv" {
|
||||||
|
errors.LogDebug(ctx, "query SRV record for "+dest.Address.String())
|
||||||
|
parts := strings.SplitN(dest.Address.String(), ".", 3)
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return nil, errors.New("invalid address format", dest.Address.String())
|
||||||
|
}
|
||||||
|
_, srvRecords, err := gonet.DefaultResolver.LookupSRV(context.Background(), parts[0][1:], parts[1][1:], parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to lookup SRV record").Base(err)
|
||||||
|
}
|
||||||
|
errors.LogDebug(ctx, "SRV record: "+fmt.Sprintf("addr=%s, port=%d, priority=%d, weight=%d", srvRecords[0].Target, srvRecords[0].Port, srvRecords[0].Priority, srvRecords[0].Weight))
|
||||||
|
if OverridePort {
|
||||||
|
newDest.Port = net.Port(srvRecords[0].Port)
|
||||||
|
}
|
||||||
|
if OverrideAddress {
|
||||||
|
newDest.Address = net.ParseAddress(srvRecords[0].Target)
|
||||||
|
}
|
||||||
|
return &newDest, nil
|
||||||
|
}
|
||||||
|
if OverrideBy == "txt" {
|
||||||
|
errors.LogDebug(ctx, "query TXT record for "+dest.Address.String())
|
||||||
|
txtRecords, err := gonet.DefaultResolver.LookupTXT(ctx, dest.Address.String())
|
||||||
|
if err != nil {
|
||||||
|
errors.LogError(ctx, "failed to lookup SRV record: "+err.Error())
|
||||||
|
return nil, errors.New("failed to lookup SRV record").Base(err)
|
||||||
|
}
|
||||||
|
for _, txtRecord := range txtRecords {
|
||||||
|
errors.LogDebug(ctx, "TXT record: "+txtRecord)
|
||||||
|
addr_s, port_s, _ := net.SplitHostPort(string(txtRecord))
|
||||||
|
addr := net.ParseAddress(addr_s)
|
||||||
|
port, err := net.PortFromString(port_s)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if OverridePort {
|
||||||
|
newDest.Port = port
|
||||||
|
}
|
||||||
|
if OverrideAddress {
|
||||||
|
newDest.Address = addr
|
||||||
|
}
|
||||||
|
return &newDest, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DialSystem calls system dialer to create a network connection.
|
// DialSystem calls system dialer to create a network connection.
|
||||||
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
||||||
var src net.Address
|
var src net.Address
|
||||||
@@ -152,6 +242,11 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig
|
|||||||
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
|
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newDest, err := checkAddressPortStrategy(ctx, dest, sockopt); err == nil && newDest != nil {
|
||||||
|
errors.LogInfo(ctx, "replace destination with "+newDest.String())
|
||||||
|
dest = *newDest
|
||||||
|
}
|
||||||
|
|
||||||
if canLookupIP(ctx, dest, sockopt) {
|
if canLookupIP(ctx, dest, sockopt) {
|
||||||
ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
|
ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
|
||||||
if err == nil && len(ips) > 0 {
|
if err == nil && len(ips) > 0 {
|
||||||
|
@@ -8,7 +8,6 @@ import (
|
|||||||
"crypto/ecdh"
|
"crypto/ecdh"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
@@ -16,7 +15,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -27,6 +25,7 @@ import (
|
|||||||
|
|
||||||
utls "github.com/refraction-networking/utls"
|
utls "github.com/refraction-networking/utls"
|
||||||
"github.com/xtls/reality"
|
"github.com/xtls/reality"
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
@@ -180,12 +179,12 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
prefix := []byte("https://" + uConn.ServerName)
|
prefix := []byte("https://" + uConn.ServerName)
|
||||||
maps.Lock()
|
maps.Lock()
|
||||||
if maps.maps == nil {
|
if maps.maps == nil {
|
||||||
maps.maps = make(map[string]map[string]bool)
|
maps.maps = make(map[string]map[string]struct{})
|
||||||
}
|
}
|
||||||
paths := maps.maps[uConn.ServerName]
|
paths := maps.maps[uConn.ServerName]
|
||||||
if paths == nil {
|
if paths == nil {
|
||||||
paths = make(map[string]bool)
|
paths = make(map[string]struct{})
|
||||||
paths[config.SpiderX] = true
|
paths[config.SpiderX] = struct{}{}
|
||||||
maps.maps[uConn.ServerName] = paths
|
maps.maps[uConn.ServerName] = paths
|
||||||
}
|
}
|
||||||
firstURL := string(prefix) + getPathLocked(paths)
|
firstURL := string(prefix) + getPathLocked(paths)
|
||||||
@@ -213,13 +212,13 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
}
|
}
|
||||||
times := 1
|
times := 1
|
||||||
if !first {
|
if !first {
|
||||||
times = int(randBetween(config.SpiderY[4], config.SpiderY[5]))
|
times = int(crypto.RandBetween(config.SpiderY[4], config.SpiderY[5]))
|
||||||
}
|
}
|
||||||
for j := 0; j < times; j++ {
|
for j := 0; j < times; j++ {
|
||||||
if !first && j == 0 {
|
if !first && j == 0 {
|
||||||
req.Header.Set("Referer", firstURL)
|
req.Header.Set("Referer", firstURL)
|
||||||
}
|
}
|
||||||
req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(randBetween(config.SpiderY[0], config.SpiderY[1])))})
|
req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(crypto.RandBetween(config.SpiderY[0], config.SpiderY[1])))})
|
||||||
if resp, err = client.Do(req); err != nil {
|
if resp, err = client.Do(req); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -232,7 +231,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
for _, m := range href.FindAllSubmatch(body, -1) {
|
for _, m := range href.FindAllSubmatch(body, -1) {
|
||||||
m[1] = bytes.TrimPrefix(m[1], prefix)
|
m[1] = bytes.TrimPrefix(m[1], prefix)
|
||||||
if !bytes.Contains(m[1], dot) {
|
if !bytes.Contains(m[1], dot) {
|
||||||
paths[string(m[1])] = true
|
paths[string(m[1])] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req.URL.Path = getPathLocked(paths)
|
req.URL.Path = getPathLocked(paths)
|
||||||
@@ -243,18 +242,18 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
}
|
}
|
||||||
maps.Unlock()
|
maps.Unlock()
|
||||||
if !first {
|
if !first {
|
||||||
time.Sleep(time.Duration(randBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval
|
time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get(true)
|
get(true)
|
||||||
concurrency := int(randBetween(config.SpiderY[2], config.SpiderY[3]))
|
concurrency := int(crypto.RandBetween(config.SpiderY[2], config.SpiderY[3]))
|
||||||
for i := 0; i < concurrency; i++ {
|
for i := 0; i < concurrency; i++ {
|
||||||
go get(false)
|
go get(false)
|
||||||
}
|
}
|
||||||
// Do not close the connection
|
// Do not close the connection
|
||||||
}()
|
}()
|
||||||
time.Sleep(time.Duration(randBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return
|
time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return
|
||||||
return nil, errors.New("REALITY: processed invalid connection").AtWarning()
|
return nil, errors.New("REALITY: processed invalid connection").AtWarning()
|
||||||
}
|
}
|
||||||
return uConn, nil
|
return uConn, nil
|
||||||
@@ -267,11 +266,11 @@ var (
|
|||||||
|
|
||||||
var maps struct {
|
var maps struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
maps map[string]map[string]bool
|
maps map[string]map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPathLocked(paths map[string]bool) string {
|
func getPathLocked(paths map[string]struct{}) string {
|
||||||
stopAt := int(randBetween(0, int64(len(paths)-1)))
|
stopAt := int(crypto.RandBetween(0, int64(len(paths)-1)))
|
||||||
i := 0
|
i := 0
|
||||||
for s := range paths {
|
for s := range paths {
|
||||||
if i == stopAt {
|
if i == stopAt {
|
||||||
@@ -281,11 +280,3 @@ func getPathLocked(paths map[string]bool) string {
|
|||||||
}
|
}
|
||||||
return "/"
|
return "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
func randBetween(left int64, right int64) int64 {
|
|
||||||
if left == right {
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left))
|
|
||||||
return left + bigInt.Int64()
|
|
||||||
}
|
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
package splithttp
|
package splithttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"math/big"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,13 +33,12 @@ func (c *Config) GetNormalizedQuery() string {
|
|||||||
query = pathAndQuery[1]
|
query = pathAndQuery[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if query != "" {
|
if query != "" {
|
||||||
query += "&"
|
query += "&"
|
||||||
}
|
}
|
||||||
|
query += "x_version=" + core.Version()
|
||||||
// query += "x_version=" + core.Version()
|
*/
|
||||||
|
|
||||||
query += "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().From))
|
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
@@ -185,9 +183,5 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c RangeConfig) rand() int32 {
|
func (c RangeConfig) rand() int32 {
|
||||||
if c.From == c.To {
|
return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
|
||||||
return c.From
|
|
||||||
}
|
|
||||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(c.To-c.From)))
|
|
||||||
return c.From + int32(bigInt.Int64())
|
|
||||||
}
|
}
|
||||||
|
@@ -30,16 +30,6 @@ import (
|
|||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defines the maximum time an idle TCP session can survive in the tunnel, so
|
|
||||||
// it should be consistent across HTTP versions and with other transports.
|
|
||||||
const connIdleTimeout = 300 * time.Second
|
|
||||||
|
|
||||||
// consistent with quic-go
|
|
||||||
const quicgoH3KeepAlivePeriod = 10 * time.Second
|
|
||||||
|
|
||||||
// consistent with chrome
|
|
||||||
const chromeH2KeepAlivePeriod = 45 * time.Second
|
|
||||||
|
|
||||||
type dialerConf struct {
|
type dialerConf struct {
|
||||||
net.Destination
|
net.Destination
|
||||||
*internet.MemoryStreamConfig
|
*internet.MemoryStreamConfig
|
||||||
@@ -154,13 +144,13 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
|
|
||||||
if httpVersion == "3" {
|
if httpVersion == "3" {
|
||||||
if keepAlivePeriod == 0 {
|
if keepAlivePeriod == 0 {
|
||||||
keepAlivePeriod = quicgoH3KeepAlivePeriod
|
keepAlivePeriod = net.QuicgoH3KeepAlivePeriod
|
||||||
}
|
}
|
||||||
if keepAlivePeriod < 0 {
|
if keepAlivePeriod < 0 {
|
||||||
keepAlivePeriod = 0
|
keepAlivePeriod = 0
|
||||||
}
|
}
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
MaxIdleTimeout: connIdleTimeout,
|
MaxIdleTimeout: net.ConnIdleTimeout,
|
||||||
|
|
||||||
// these two are defaults of quic-go/http3. the default of quic-go (no
|
// these two are defaults of quic-go/http3. the default of quic-go (no
|
||||||
// http3) is different, so it is hardcoded here for clarity.
|
// http3) is different, so it is hardcoded here for clarity.
|
||||||
@@ -168,7 +158,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
MaxIncomingStreams: -1,
|
MaxIncomingStreams: -1,
|
||||||
KeepAlivePeriod: keepAlivePeriod,
|
KeepAlivePeriod: keepAlivePeriod,
|
||||||
}
|
}
|
||||||
transport = &http3.RoundTripper{
|
transport = &http3.Transport{
|
||||||
QUICConfig: quicConfig,
|
QUICConfig: quicConfig,
|
||||||
TLSClientConfig: gotlsConfig,
|
TLSClientConfig: gotlsConfig,
|
||||||
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||||
@@ -198,7 +188,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
udpConn = &internet.FakePacketConn{c}
|
udpConn = &internet.FakePacketConn{Conn: c}
|
||||||
udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
|
udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -210,7 +200,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
}
|
}
|
||||||
} else if httpVersion == "2" {
|
} else if httpVersion == "2" {
|
||||||
if keepAlivePeriod == 0 {
|
if keepAlivePeriod == 0 {
|
||||||
keepAlivePeriod = chromeH2KeepAlivePeriod
|
keepAlivePeriod = net.ChromeH2KeepAlivePeriod
|
||||||
}
|
}
|
||||||
if keepAlivePeriod < 0 {
|
if keepAlivePeriod < 0 {
|
||||||
keepAlivePeriod = 0
|
keepAlivePeriod = 0
|
||||||
@@ -219,7 +209,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
|
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
|
||||||
return dialContext(ctxInner)
|
return dialContext(ctxInner)
|
||||||
},
|
},
|
||||||
IdleConnTimeout: connIdleTimeout,
|
IdleConnTimeout: net.ConnIdleTimeout,
|
||||||
ReadIdleTimeout: keepAlivePeriod,
|
ReadIdleTimeout: keepAlivePeriod,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -230,7 +220,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
transport = &http.Transport{
|
transport = &http.Transport{
|
||||||
DialTLSContext: httpDialContext,
|
DialTLSContext: httpDialContext,
|
||||||
DialContext: httpDialContext,
|
DialContext: httpDialContext,
|
||||||
IdleConnTimeout: connIdleTimeout,
|
IdleConnTimeout: net.ConnIdleTimeout,
|
||||||
// chunked transfer download with KeepAlives is buggy with
|
// chunked transfer download with KeepAlives is buggy with
|
||||||
// http.Client and our custom dial context.
|
// http.Client and our custom dial context.
|
||||||
DisableKeepAlives: true,
|
DisableKeepAlives: true,
|
||||||
|
@@ -3,9 +3,8 @@ package splithttp
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
gotls "crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
gonet "net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -24,9 +23,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/reality"
|
"github.com/xtls/xray-core/transport/internet/reality"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
v2tls "github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
"golang.org/x/net/http2"
|
|
||||||
"golang.org/x/net/http2/h2c"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type requestHandler struct {
|
type requestHandler struct {
|
||||||
@@ -36,7 +33,7 @@ type requestHandler struct {
|
|||||||
ln *Listener
|
ln *Listener
|
||||||
sessionMu *sync.Mutex
|
sessionMu *sync.Mutex
|
||||||
sessions sync.Map
|
sessions sync.Map
|
||||||
localAddr gonet.TCPAddr
|
localAddr net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpSession struct {
|
type httpSession struct {
|
||||||
@@ -48,21 +45,6 @@ type httpSession struct {
|
|||||||
isFullyConnected *done.Instance
|
isFullyConnected *done.Instance
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *requestHandler) maybeReapSession(isFullyConnected *done.Instance, sessionId string) {
|
|
||||||
shouldReap := done.New()
|
|
||||||
go func() {
|
|
||||||
time.Sleep(30 * time.Second)
|
|
||||||
shouldReap.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-isFullyConnected.Wait():
|
|
||||||
return
|
|
||||||
case <-shouldReap.Wait():
|
|
||||||
h.sessions.Delete(sessionId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
||||||
// fast path
|
// fast path
|
||||||
currentSessionAny, ok := h.sessions.Load(sessionId)
|
currentSessionAny, ok := h.sessions.Load(sessionId)
|
||||||
@@ -85,7 +67,21 @@ func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.sessions.Store(sessionId, s)
|
h.sessions.Store(sessionId, s)
|
||||||
go h.maybeReapSession(s.isFullyConnected, sessionId)
|
|
||||||
|
shouldReap := done.New()
|
||||||
|
go func() {
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
shouldReap.Close()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-shouldReap.Wait():
|
||||||
|
h.sessions.Delete(sessionId)
|
||||||
|
s.uploadQueue.Close()
|
||||||
|
case <-s.isFullyConnected.Wait():
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,14 +140,25 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
|
forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
|
||||||
remoteAddr, err := gonet.ResolveTCPAddr("tcp", request.RemoteAddr)
|
var remoteAddr net.Addr
|
||||||
|
var err error
|
||||||
|
remoteAddr, err = net.ResolveTCPAddr("tcp", request.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remoteAddr = &gonet.TCPAddr{}
|
remoteAddr = &net.TCPAddr{
|
||||||
|
IP: []byte{0, 0, 0, 0},
|
||||||
|
Port: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if request.ProtoMajor == 3 {
|
||||||
|
remoteAddr = &net.UDPAddr{
|
||||||
|
IP: remoteAddr.(*net.TCPAddr).IP,
|
||||||
|
Port: remoteAddr.(*net.TCPAddr).Port,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
|
if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
|
||||||
remoteAddr = &net.TCPAddr{
|
remoteAddr = &net.TCPAddr{
|
||||||
IP: forwardedAddrs[0].IP(),
|
IP: forwardedAddrs[0].IP(),
|
||||||
Port: int(0),
|
Port: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +168,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To)
|
scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To)
|
||||||
|
|
||||||
if request.Method == "POST" && sessionId != "" {
|
if request.Method == "POST" && sessionId != "" { // stream-up, packet-up
|
||||||
seq := ""
|
seq := ""
|
||||||
if len(subpath) > 1 {
|
if len(subpath) > 1 {
|
||||||
seq = subpath[1]
|
seq = subpath[1]
|
||||||
@@ -173,8 +180,13 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
writer.WriteHeader(http.StatusBadRequest)
|
writer.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = currentSession.uploadQueue.Push(Packet{
|
httpSC := &httpServerConn{
|
||||||
|
Instance: done.New(),
|
||||||
Reader: request.Body,
|
Reader: request.Body,
|
||||||
|
ResponseWriter: writer,
|
||||||
|
}
|
||||||
|
err = currentSession.uploadQueue.Push(Packet{
|
||||||
|
Reader: httpSC,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogInfoInner(context.Background(), err, "failed to upload (PushReader)")
|
errors.LogInfoInner(context.Background(), err, "failed to upload (PushReader)")
|
||||||
@@ -186,21 +198,21 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
scStreamUpServerSecs := h.config.GetNormalizedScStreamUpServerSecs()
|
scStreamUpServerSecs := h.config.GetNormalizedScStreamUpServerSecs()
|
||||||
if referrer != "" && scStreamUpServerSecs.To > 0 {
|
if referrer != "" && scStreamUpServerSecs.To > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
|
||||||
recover()
|
|
||||||
}()
|
|
||||||
for {
|
for {
|
||||||
_, err := writer.Write(bytes.Repeat([]byte{'X'}, int(h.config.GetNormalizedXPaddingBytes().rand())))
|
_, err := httpSC.Write(bytes.Repeat([]byte{'X'}, int(h.config.GetNormalizedXPaddingBytes().rand())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
writer.(http.Flusher).Flush()
|
|
||||||
time.Sleep(time.Duration(scStreamUpServerSecs.rand()) * time.Second)
|
time.Sleep(time.Duration(scStreamUpServerSecs.rand()) * time.Second)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
<-request.Context().Done()
|
select {
|
||||||
|
case <-request.Context().Done():
|
||||||
|
case <-httpSC.Wait():
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
httpSC.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,12 +255,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
} else if request.Method == "GET" || sessionId == "" {
|
} else if request.Method == "GET" || sessionId == "" { // stream-down, stream-one
|
||||||
responseFlusher, ok := writer.(http.Flusher)
|
|
||||||
if !ok {
|
|
||||||
panic("expected http.ResponseWriter to be an http.Flusher")
|
|
||||||
}
|
|
||||||
|
|
||||||
if sessionId != "" {
|
if sessionId != "" {
|
||||||
// after GET is done, the connection is finished. disable automatic
|
// after GET is done, the connection is finished. disable automatic
|
||||||
// session reaping, and handle it in defer
|
// session reaping, and handle it in defer
|
||||||
@@ -269,21 +276,20 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
writer.(http.Flusher).Flush()
|
||||||
|
|
||||||
responseFlusher.Flush()
|
httpSC := &httpServerConn{
|
||||||
|
Instance: done.New(),
|
||||||
downloadDone := done.New()
|
Reader: request.Body,
|
||||||
|
ResponseWriter: writer,
|
||||||
conn := splitConn{
|
|
||||||
writer: &httpResponseBodyWriter{
|
|
||||||
responseWriter: writer,
|
|
||||||
downloadDone: downloadDone,
|
|
||||||
responseFlusher: responseFlusher,
|
|
||||||
},
|
|
||||||
reader: request.Body,
|
|
||||||
remoteAddr: remoteAddr,
|
|
||||||
}
|
}
|
||||||
if sessionId != "" {
|
conn := splitConn{
|
||||||
|
writer: httpSC,
|
||||||
|
reader: httpSC,
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
localAddr: h.localAddr,
|
||||||
|
}
|
||||||
|
if sessionId != "" { // if not stream-one
|
||||||
conn.reader = currentSession.uploadQueue
|
conn.reader = currentSession.uploadQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +298,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
// "A ResponseWriter may not be used after [Handler.ServeHTTP] has returned."
|
// "A ResponseWriter may not be used after [Handler.ServeHTTP] has returned."
|
||||||
select {
|
select {
|
||||||
case <-request.Context().Done():
|
case <-request.Context().Done():
|
||||||
case <-downloadDone.Wait():
|
case <-httpSC.Wait():
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.Close()
|
conn.Close()
|
||||||
@@ -302,31 +308,30 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpResponseBodyWriter struct {
|
type httpServerConn struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
responseWriter http.ResponseWriter
|
*done.Instance
|
||||||
responseFlusher http.Flusher
|
io.Reader // no need to Close request.Body
|
||||||
downloadDone *done.Instance
|
http.ResponseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpResponseBodyWriter) Write(b []byte) (int, error) {
|
func (c *httpServerConn) Write(b []byte) (int, error) {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
if c.downloadDone.Done() {
|
if c.Done() {
|
||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
}
|
}
|
||||||
n, err := c.responseWriter.Write(b)
|
n, err := c.ResponseWriter.Write(b)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.responseFlusher.Flush()
|
c.ResponseWriter.(http.Flusher).Flush()
|
||||||
}
|
}
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpResponseBodyWriter) Close() error {
|
func (c *httpServerConn) Close() error {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
c.downloadDone.Close()
|
return c.Instance.Close()
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
@@ -340,34 +345,30 @@ type Listener struct {
|
|||||||
isH3 bool
|
isH3 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
|
func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
|
||||||
l := &Listener{
|
l := &Listener{
|
||||||
addConn: addConn,
|
addConn: addConn,
|
||||||
}
|
}
|
||||||
shSettings := streamSettings.ProtocolSettings.(*Config)
|
l.config = streamSettings.ProtocolSettings.(*Config)
|
||||||
l.config = shSettings
|
|
||||||
if l.config != nil {
|
if l.config != nil {
|
||||||
if streamSettings.SocketSettings == nil {
|
if streamSettings.SocketSettings == nil {
|
||||||
streamSettings.SocketSettings = &internet.SocketConfig{}
|
streamSettings.SocketSettings = &internet.SocketConfig{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var listener net.Listener
|
|
||||||
var err error
|
|
||||||
var localAddr = gonet.TCPAddr{}
|
|
||||||
handler := &requestHandler{
|
handler := &requestHandler{
|
||||||
config: shSettings,
|
config: l.config,
|
||||||
host: shSettings.Host,
|
host: l.config.Host,
|
||||||
path: shSettings.GetNormalizedPath(),
|
path: l.config.GetNormalizedPath(),
|
||||||
ln: l,
|
ln: l,
|
||||||
sessionMu: &sync.Mutex{},
|
sessionMu: &sync.Mutex{},
|
||||||
sessions: sync.Map{},
|
sessions: sync.Map{},
|
||||||
localAddr: localAddr,
|
|
||||||
}
|
}
|
||||||
tlsConfig := getTLSConfig(streamSettings)
|
tlsConfig := getTLSConfig(streamSettings)
|
||||||
l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
|
l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
|
||||||
|
|
||||||
|
var err error
|
||||||
if port == net.Port(0) { // unix
|
if port == net.Port(0) { // unix
|
||||||
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
l.listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
||||||
Name: address.Domain(),
|
Name: address.Domain(),
|
||||||
Net: "unix",
|
Net: "unix",
|
||||||
}, streamSettings.SocketSettings)
|
}, streamSettings.SocketSettings)
|
||||||
@@ -383,27 +384,24 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to listen UDP for XHTTP/3 on ", address, ":", port).Base(err)
|
return nil, errors.New("failed to listen UDP for XHTTP/3 on ", address, ":", port).Base(err)
|
||||||
}
|
}
|
||||||
h3listener, err := quic.ListenEarly(Conn, tlsConfig, nil)
|
l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err)
|
return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err)
|
||||||
}
|
}
|
||||||
l.h3listener = h3listener
|
|
||||||
errors.LogInfo(ctx, "listening QUIC for XHTTP/3 on ", address, ":", port)
|
errors.LogInfo(ctx, "listening QUIC for XHTTP/3 on ", address, ":", port)
|
||||||
|
|
||||||
|
handler.localAddr = l.h3listener.Addr()
|
||||||
|
|
||||||
l.h3server = &http3.Server{
|
l.h3server = &http3.Server{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
||||||
errors.LogWarningInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3")
|
errors.LogErrorInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else { // tcp
|
} else { // tcp
|
||||||
localAddr = gonet.TCPAddr{
|
l.listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
|
||||||
IP: address.IP(),
|
|
||||||
Port: int(port),
|
|
||||||
}
|
|
||||||
listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
|
|
||||||
IP: address.IP(),
|
IP: address.IP(),
|
||||||
Port: int(port),
|
Port: int(port),
|
||||||
}, streamSettings.SocketSettings)
|
}, streamSettings.SocketSettings)
|
||||||
@@ -414,29 +412,31 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tcp/unix (h1/h2)
|
// tcp/unix (h1/h2)
|
||||||
if listener != nil {
|
if l.listener != nil {
|
||||||
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||||
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
|
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
|
||||||
listener = tls.NewListener(listener, tlsConfig)
|
l.listener = gotls.NewListener(l.listener, tlsConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
|
if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||||
listener = goreality.NewListener(listener, config.GetREALITYConfig())
|
l.listener = goreality.NewListener(l.listener, config.GetREALITYConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
handler.localAddr = l.listener.Addr()
|
||||||
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
|
||||||
l.listener = listener
|
// server can handle both plaintext HTTP/1.1 and h2c
|
||||||
|
protocols := new(http.Protocols)
|
||||||
|
protocols.SetHTTP1(true)
|
||||||
|
protocols.SetUnencryptedHTTP2(true)
|
||||||
l.server = http.Server{
|
l.server = http.Server{
|
||||||
Handler: h2cHandler,
|
Handler: handler,
|
||||||
ReadHeaderTimeout: time.Second * 4,
|
ReadHeaderTimeout: time.Second * 4,
|
||||||
MaxHeaderBytes: 8192,
|
MaxHeaderBytes: 8192,
|
||||||
|
Protocols: protocols,
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := l.server.Serve(l.listener); err != nil {
|
if err := l.server.Serve(l.listener); err != nil {
|
||||||
errors.LogWarningInner(ctx, err, "failed to serve HTTP for XHTTP")
|
errors.LogErrorInner(ctx, err, "failed to serve HTTP for XHTTP")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -466,13 +466,13 @@ func (ln *Listener) Close() error {
|
|||||||
}
|
}
|
||||||
return errors.New("listener does not have an HTTP/3 server or a net.listener")
|
return errors.New("listener does not have an HTTP/3 server or a net.listener")
|
||||||
}
|
}
|
||||||
func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *tls.Config {
|
func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *gotls.Config {
|
||||||
config := v2tls.ConfigFromStreamSettings(streamSettings)
|
config := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return &tls.Config{}
|
return &gotls.Config{}
|
||||||
}
|
}
|
||||||
return config.GetTLSConfig()
|
return config.GetTLSConfig()
|
||||||
}
|
}
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
|
common.Must(internet.RegisterTransportListener(protocolName, ListenXH))
|
||||||
}
|
}
|
||||||
|
@@ -3,10 +3,8 @@ package splithttp_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
gotls "crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
gonet "net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -23,12 +21,11 @@ import (
|
|||||||
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
"golang.org/x/net/http2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_listenSHAndDial(t *testing.T) {
|
func Test_ListenXHAndDial(t *testing.T) {
|
||||||
listenPort := tcp.PickPort()
|
listenPort := tcp.PickPort()
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
||||||
ProtocolName: "splithttp",
|
ProtocolName: "splithttp",
|
||||||
ProtocolSettings: &Config{
|
ProtocolSettings: &Config{
|
||||||
Path: "/sh",
|
Path: "/sh",
|
||||||
@@ -85,7 +82,7 @@ func Test_listenSHAndDial(t *testing.T) {
|
|||||||
|
|
||||||
func TestDialWithRemoteAddr(t *testing.T) {
|
func TestDialWithRemoteAddr(t *testing.T) {
|
||||||
listenPort := tcp.PickPort()
|
listenPort := tcp.PickPort()
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
||||||
ProtocolName: "splithttp",
|
ProtocolName: "splithttp",
|
||||||
ProtocolSettings: &Config{
|
ProtocolSettings: &Config{
|
||||||
Path: "sh",
|
Path: "sh",
|
||||||
@@ -125,7 +122,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
|||||||
common.Must(listen.Close())
|
common.Must(listen.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_listenSHAndDial_TLS(t *testing.T) {
|
func Test_ListenXHAndDial_TLS(t *testing.T) {
|
||||||
if runtime.GOARCH == "arm64" {
|
if runtime.GOARCH == "arm64" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -145,7 +142,7 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
|
|||||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||||
go func() {
|
go func() {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
@@ -180,7 +177,7 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_listenSHAndDial_H2C(t *testing.T) {
|
func Test_ListenXHAndDial_H2C(t *testing.T) {
|
||||||
if runtime.GOARCH == "arm64" {
|
if runtime.GOARCH == "arm64" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -193,7 +190,7 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
|||||||
Path: "shs",
|
Path: "shs",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||||
go func() {
|
go func() {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
}()
|
}()
|
||||||
@@ -201,17 +198,11 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
defer listen.Close()
|
defer listen.Close()
|
||||||
|
|
||||||
|
protocols := new(http.Protocols)
|
||||||
|
protocols.SetUnencryptedHTTP2(true)
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Transport: &http2.Transport{
|
Transport: &http.Transport{
|
||||||
// So http2.Transport doesn't complain the URL scheme isn't 'https'
|
Protocols: protocols,
|
||||||
AllowHTTP: true,
|
|
||||||
// even with AllowHTTP, http2.Transport will attempt to establish
|
|
||||||
// the connection using DialTLSContext. Disable TLS with custom
|
|
||||||
// dial context.
|
|
||||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (gonet.Conn, error) {
|
|
||||||
var d gonet.Dialer
|
|
||||||
return d.DialContext(ctx, network, addr)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +218,7 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_listenSHAndDial_QUIC(t *testing.T) {
|
func Test_ListenXHAndDial_QUIC(t *testing.T) {
|
||||||
if runtime.GOARCH == "arm64" {
|
if runtime.GOARCH == "arm64" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -250,7 +241,7 @@ func Test_listenSHAndDial_QUIC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
serverClosed := false
|
serverClosed := false
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||||
go func() {
|
go func() {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
@@ -309,11 +300,11 @@ func Test_listenSHAndDial_QUIC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_listenSHAndDial_Unix(t *testing.T) {
|
func Test_ListenXHAndDial_Unix(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
tempSocket := tempDir + "/server.sock"
|
tempSocket := tempDir + "/server.sock"
|
||||||
|
|
||||||
listen, err := ListenSH(context.Background(), net.DomainAddress(tempSocket), 0, &internet.MemoryStreamConfig{
|
listen, err := ListenXH(context.Background(), net.DomainAddress(tempSocket), 0, &internet.MemoryStreamConfig{
|
||||||
ProtocolName: "splithttp",
|
ProtocolName: "splithttp",
|
||||||
ProtocolSettings: &Config{
|
ProtocolSettings: &Config{
|
||||||
Path: "/sh",
|
Path: "/sh",
|
||||||
@@ -373,7 +364,7 @@ func Test_listenSHAndDial_Unix(t *testing.T) {
|
|||||||
|
|
||||||
func Test_queryString(t *testing.T) {
|
func Test_queryString(t *testing.T) {
|
||||||
listenPort := tcp.PickPort()
|
listenPort := tcp.PickPort()
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
||||||
ProtocolName: "splithttp",
|
ProtocolName: "splithttp",
|
||||||
ProtocolSettings: &Config{
|
ProtocolSettings: &Config{
|
||||||
// this querystring does not have any effect, but sometimes people blindly copy it from websocket config. make sure the outbound doesn't break
|
// this querystring does not have any effect, but sometimes people blindly copy it from websocket config. make sure the outbound doesn't break
|
||||||
@@ -431,7 +422,7 @@ func Test_maxUpload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var uploadSize int
|
var uploadSize int
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||||
go func(c stat.Connection) {
|
go func(c stat.Connection) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
var b [10240]byte
|
var b [10240]byte
|
||||||
|
@@ -20,6 +20,7 @@ type Packet struct {
|
|||||||
|
|
||||||
type uploadQueue struct {
|
type uploadQueue struct {
|
||||||
reader io.ReadCloser
|
reader io.ReadCloser
|
||||||
|
nomore bool
|
||||||
pushedPackets chan Packet
|
pushedPackets chan Packet
|
||||||
writeCloseMutex sync.Mutex
|
writeCloseMutex sync.Mutex
|
||||||
heap uploadHeap
|
heap uploadHeap
|
||||||
@@ -42,19 +43,15 @@ func (h *uploadQueue) Push(p Packet) error {
|
|||||||
h.writeCloseMutex.Lock()
|
h.writeCloseMutex.Lock()
|
||||||
defer h.writeCloseMutex.Unlock()
|
defer h.writeCloseMutex.Unlock()
|
||||||
|
|
||||||
runtime.Gosched()
|
|
||||||
if h.reader != nil && p.Reader != nil {
|
|
||||||
p.Reader.Close()
|
|
||||||
return errors.New("h.reader already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.closed {
|
if h.closed {
|
||||||
if p.Reader != nil {
|
|
||||||
p.Reader.Close()
|
|
||||||
}
|
|
||||||
return errors.New("packet queue closed")
|
return errors.New("packet queue closed")
|
||||||
}
|
}
|
||||||
|
if h.nomore {
|
||||||
|
return errors.New("h.reader already exists")
|
||||||
|
}
|
||||||
|
if p.Reader != nil {
|
||||||
|
h.nomore = true
|
||||||
|
}
|
||||||
h.pushedPackets <- p
|
h.pushedPackets <- p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -65,9 +62,20 @@ func (h *uploadQueue) Close() error {
|
|||||||
|
|
||||||
if !h.closed {
|
if !h.closed {
|
||||||
h.closed = true
|
h.closed = true
|
||||||
|
runtime.Gosched() // hope Read() gets the packet
|
||||||
|
f:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case p := <-h.pushedPackets:
|
||||||
|
if p.Reader != nil {
|
||||||
|
h.reader = p.Reader
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break f
|
||||||
|
}
|
||||||
|
}
|
||||||
close(h.pushedPackets)
|
close(h.pushedPackets)
|
||||||
}
|
}
|
||||||
runtime.Gosched()
|
|
||||||
if h.reader != nil {
|
if h.reader != nil {
|
||||||
return h.reader.Close()
|
return h.reader.Close()
|
||||||
}
|
}
|
||||||
|
@@ -21,19 +21,6 @@ type DefaultListener struct {
|
|||||||
controllers []control.Func
|
controllers []control.Func
|
||||||
}
|
}
|
||||||
|
|
||||||
type combinedListener struct {
|
|
||||||
net.Listener
|
|
||||||
locker *FileLocker // for unix domain socket
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cl *combinedListener) Close() error {
|
|
||||||
if cl.locker != nil {
|
|
||||||
cl.locker.Release()
|
|
||||||
cl.locker = nil
|
|
||||||
}
|
|
||||||
return cl.Listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []control.Func) func(network, address string, c syscall.RawConn) error {
|
func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []control.Func) func(network, address string, c syscall.RawConn) error {
|
||||||
return func(network, address string, c syscall.RawConn) error {
|
return func(network, address string, c syscall.RawConn) error {
|
||||||
return c.Control(func(fd uintptr) {
|
return c.Control(func(fd uintptr) {
|
||||||
@@ -54,6 +41,40 @@ func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For some reason, other component of ray will assume the listener is a TCP listener and have valid remote address.
|
||||||
|
// But in fact it doesn't. So we need to wrap the listener to make it return 0.0.0.0(unspecified) as remote address.
|
||||||
|
// If other issues encountered, we should able to fix it here.
|
||||||
|
type UnixListenerWrapper struct {
|
||||||
|
*net.UnixListener
|
||||||
|
locker *FileLocker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UnixListenerWrapper) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.UnixListener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &UnixConnWrapper{UnixConn: conn.(*net.UnixConn)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UnixListenerWrapper) Close() error {
|
||||||
|
if l.locker != nil {
|
||||||
|
l.locker.Release()
|
||||||
|
l.locker = nil
|
||||||
|
}
|
||||||
|
return l.UnixListener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnixConnWrapper struct {
|
||||||
|
*net.UnixConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *UnixConnWrapper) RemoteAddr() net.Addr {
|
||||||
|
return &net.TCPAddr{
|
||||||
|
IP: []byte{0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (l net.Listener, err error) {
|
func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (l net.Listener, err error) {
|
||||||
var lc net.ListenConfig
|
var lc net.ListenConfig
|
||||||
var network, address string
|
var network, address string
|
||||||
@@ -113,9 +134,9 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
|
|||||||
callback = func(l net.Listener, err error) (net.Listener, error) {
|
callback = func(l net.Listener, err error) (net.Listener, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
locker.Release()
|
locker.Release()
|
||||||
return l, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l = &combinedListener{Listener: l, locker: locker}
|
l = &UnixListenerWrapper{UnixListener: l.(*net.UnixListener), locker: locker}
|
||||||
if filePerm == nil {
|
if filePerm == nil {
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
@@ -129,9 +150,8 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err = lc.Listen(ctx, network, address)
|
l, err = callback(lc.Listen(ctx, network, address))
|
||||||
l, err = callback(l, err)
|
if err == nil && sockopt != nil && sockopt.AcceptProxyProtocol {
|
||||||
if sockopt != nil && sockopt.AcceptProxyProtocol {
|
|
||||||
policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
|
policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
|
||||||
l = &proxyproto.Listener{Listener: l, Policy: policyFunc}
|
l = &proxyproto.Listener{Listener: l, Policy: policyFunc}
|
||||||
}
|
}
|
||||||
|
@@ -2,16 +2,23 @@ package tcp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
|
"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/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/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/reality"
|
"github.com/xtls/xray-core/transport/internet/reality"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func IsFromMitm(str string) bool {
|
||||||
|
return strings.ToLower(str) == "frommitm"
|
||||||
|
}
|
||||||
|
|
||||||
// Dial dials a new TCP connection to the given destination.
|
// Dial dials a new TCP connection to the given destination.
|
||||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
||||||
errors.LogInfo(ctx, "dialing TCP to ", dest)
|
errors.LogInfo(ctx, "dialing TCP to ", dest)
|
||||||
@@ -21,20 +28,63 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||||
|
mitmServerName := session.MitmServerNameFromContext(ctx)
|
||||||
|
mitmAlpn11 := session.MitmAlpn11FromContext(ctx)
|
||||||
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
|
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
|
||||||
|
if IsFromMitm(tlsConfig.ServerName) {
|
||||||
|
tlsConfig.ServerName = mitmServerName
|
||||||
|
}
|
||||||
|
isFromMitmVerify := false
|
||||||
|
if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertInNames) > 0 {
|
||||||
|
for i, name := range r.VerifyPeerCertInNames {
|
||||||
|
if IsFromMitm(name) {
|
||||||
|
isFromMitmVerify = true
|
||||||
|
r.VerifyPeerCertInNames[0], r.VerifyPeerCertInNames[i] = r.VerifyPeerCertInNames[i], r.VerifyPeerCertInNames[0]
|
||||||
|
r.VerifyPeerCertInNames = r.VerifyPeerCertInNames[1:]
|
||||||
|
after := mitmServerName
|
||||||
|
for {
|
||||||
|
if len(after) > 0 {
|
||||||
|
r.VerifyPeerCertInNames = append(r.VerifyPeerCertInNames, after)
|
||||||
|
}
|
||||||
|
_, after, _ = strings.Cut(after, ".")
|
||||||
|
if !strings.Contains(after, ".") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.Reverse(r.VerifyPeerCertInNames)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isFromMitmAlpn := len(tlsConfig.NextProtos) == 1 && IsFromMitm(tlsConfig.NextProtos[0])
|
||||||
|
if isFromMitmAlpn {
|
||||||
|
if mitmAlpn11 {
|
||||||
|
tlsConfig.NextProtos[0] = "http/1.1"
|
||||||
|
} else {
|
||||||
|
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||||
|
}
|
||||||
|
}
|
||||||
if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil {
|
if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil {
|
||||||
conn = tls.UClient(conn, tlsConfig, fingerprint)
|
conn = tls.UClient(conn, tlsConfig, fingerprint)
|
||||||
if len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "http/1.1" {
|
if len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "http/1.1" { // allow manually specify
|
||||||
if err := conn.(*tls.UConn).WebsocketHandshakeContext(ctx); err != nil {
|
err = conn.(*tls.UConn).WebsocketHandshakeContext(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if err := conn.(*tls.UConn).HandshakeContext(ctx); err != nil {
|
err = conn.(*tls.UConn).HandshakeContext(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conn = tls.Client(conn, tlsConfig)
|
conn = tls.Client(conn, tlsConfig)
|
||||||
|
err = conn.(*tls.Conn).HandshakeContext(ctx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if isFromMitmVerify {
|
||||||
|
return nil, errors.New("MITM freedom RAW TLS: failed to verify Domain Fronting certificate from " + mitmServerName).Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
negotiatedProtocol := conn.(tls.Interface).NegotiatedProtocol()
|
||||||
|
if isFromMitmAlpn && !mitmAlpn11 && negotiatedProtocol != "h2" {
|
||||||
|
conn.Close()
|
||||||
|
return nil, errors.New("MITM freedom RAW TLS: unexpected Negotiated Protocol (" + negotiatedProtocol + ") with " + mitmServerName).AtWarning()
|
||||||
}
|
}
|
||||||
} else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
|
} else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||||
if conn, err = reality.UClient(conn, config, ctx, dest); err != nil {
|
if conn, err = reality.UClient(conn, config, ctx, dest); err != nil {
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -277,10 +278,35 @@ func (c *Config) parseServerName() string {
|
|||||||
return c.ServerName
|
return c.ServerName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
if c.PinnedPeerCertificateChainSha256 != nil {
|
if r.VerifyPeerCertInNames != nil {
|
||||||
|
if len(r.VerifyPeerCertInNames) > 0 {
|
||||||
|
certs := make([]*x509.Certificate, len(rawCerts))
|
||||||
|
for i, asn1Data := range rawCerts {
|
||||||
|
certs[i], _ = x509.ParseCertificate(asn1Data)
|
||||||
|
}
|
||||||
|
opts := x509.VerifyOptions{
|
||||||
|
Roots: r.RootCAs,
|
||||||
|
CurrentTime: time.Now(),
|
||||||
|
Intermediates: x509.NewCertPool(),
|
||||||
|
}
|
||||||
|
for _, cert := range certs[1:] {
|
||||||
|
opts.Intermediates.AddCert(cert)
|
||||||
|
}
|
||||||
|
for _, opts.DNSName = range r.VerifyPeerCertInNames {
|
||||||
|
if _, err := certs[0].Verify(opts); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.PinnedPeerCertificateChainSha256 == nil {
|
||||||
|
return errors.New("peer cert is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.PinnedPeerCertificateChainSha256 != nil {
|
||||||
hashValue := GenerateCertChainHash(rawCerts)
|
hashValue := GenerateCertChainHash(rawCerts)
|
||||||
for _, v := range c.PinnedPeerCertificateChainSha256 {
|
for _, v := range r.PinnedPeerCertificateChainSha256 {
|
||||||
if hmac.Equal(hashValue, v) {
|
if hmac.Equal(hashValue, v) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -288,11 +314,11 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
|
|||||||
return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue))
|
return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.PinnedPeerCertificatePublicKeySha256 != nil {
|
if r.PinnedPeerCertificatePublicKeySha256 != nil {
|
||||||
for _, v := range verifiedChains {
|
for _, v := range verifiedChains {
|
||||||
for _, cert := range v {
|
for _, cert := range v {
|
||||||
publicHash := GenerateCertPublicKeyHash(cert)
|
publicHash := GenerateCertPublicKeyHash(cert)
|
||||||
for _, c := range c.PinnedPeerCertificatePublicKeySha256 {
|
for _, c := range r.PinnedPeerCertificatePublicKeySha256 {
|
||||||
if hmac.Equal(publicHash, c) {
|
if hmac.Equal(publicHash, c) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -305,7 +331,10 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RandCarrier struct {
|
type RandCarrier struct {
|
||||||
ServerNameToVerify string
|
RootCAs *x509.CertPool
|
||||||
|
VerifyPeerCertInNames []string
|
||||||
|
PinnedPeerCertificateChainSha256 [][]byte
|
||||||
|
PinnedPeerCertificatePublicKeySha256 [][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
||||||
@@ -329,16 +358,25 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
randCarrier := &RandCarrier{
|
||||||
|
RootCAs: root,
|
||||||
|
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
|
||||||
|
PinnedPeerCertificateChainSha256: c.PinnedPeerCertificateChainSha256,
|
||||||
|
PinnedPeerCertificatePublicKeySha256: c.PinnedPeerCertificatePublicKeySha256,
|
||||||
|
}
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
Rand: &RandCarrier{
|
Rand: randCarrier,
|
||||||
ServerNameToVerify: c.ServerNameToVerify,
|
|
||||||
},
|
|
||||||
ClientSessionCache: globalSessionCache,
|
ClientSessionCache: globalSessionCache,
|
||||||
RootCAs: root,
|
RootCAs: root,
|
||||||
InsecureSkipVerify: c.AllowInsecure,
|
InsecureSkipVerify: c.AllowInsecure,
|
||||||
NextProtos: c.NextProtocol,
|
NextProtos: slices.Clone(c.NextProtocol),
|
||||||
SessionTicketsDisabled: !c.EnableSessionResumption,
|
SessionTicketsDisabled: !c.EnableSessionResumption,
|
||||||
VerifyPeerCertificate: c.verifyPeerCert,
|
VerifyPeerCertificate: randCarrier.verifyPeerCert,
|
||||||
|
}
|
||||||
|
if len(c.VerifyPeerCertInNames) > 0 {
|
||||||
|
config.InsecureSkipVerify = true
|
||||||
|
} else {
|
||||||
|
randCarrier.VerifyPeerCertInNames = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
@@ -202,20 +202,21 @@ type Config struct {
|
|||||||
// TLS Client Hello fingerprint (uTLS).
|
// TLS Client Hello fingerprint (uTLS).
|
||||||
Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
|
Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
|
||||||
RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"`
|
RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"`
|
||||||
// @Document A pinned certificate chain sha256 hash.
|
// @Document Some certificate chain sha256 hashes.
|
||||||
// @Document If the server's hash does not match this value, the connection will be aborted.
|
// @Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
|
||||||
// @Document This value replace allow_insecure.
|
|
||||||
// @Critical
|
// @Critical
|
||||||
PinnedPeerCertificateChainSha256 [][]byte `protobuf:"bytes,13,rep,name=pinned_peer_certificate_chain_sha256,json=pinnedPeerCertificateChainSha256,proto3" json:"pinned_peer_certificate_chain_sha256,omitempty"`
|
PinnedPeerCertificateChainSha256 [][]byte `protobuf:"bytes,13,rep,name=pinned_peer_certificate_chain_sha256,json=pinnedPeerCertificateChainSha256,proto3" json:"pinned_peer_certificate_chain_sha256,omitempty"`
|
||||||
// @Document A pinned certificate public key sha256 hash.
|
// @Document Some certificate public key sha256 hashes.
|
||||||
// @Document If the server's public key hash does not match this value, the connection will be aborted.
|
// @Document After normal validation (required), if one of certs in verified chain matches one of these values, the connection will be eventually accepted.
|
||||||
// @Document This value replace allow_insecure.
|
|
||||||
// @Critical
|
// @Critical
|
||||||
PinnedPeerCertificatePublicKeySha256 [][]byte `protobuf:"bytes,14,rep,name=pinned_peer_certificate_public_key_sha256,json=pinnedPeerCertificatePublicKeySha256,proto3" json:"pinned_peer_certificate_public_key_sha256,omitempty"`
|
PinnedPeerCertificatePublicKeySha256 [][]byte `protobuf:"bytes,14,rep,name=pinned_peer_certificate_public_key_sha256,json=pinnedPeerCertificatePublicKeySha256,proto3" json:"pinned_peer_certificate_public_key_sha256,omitempty"`
|
||||||
MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
|
MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
|
||||||
// Lists of string as CurvePreferences values.
|
// Lists of string as CurvePreferences values.
|
||||||
CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"`
|
CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"`
|
||||||
ServerNameToVerify string `protobuf:"bytes,17,opt,name=server_name_to_verify,json=serverNameToVerify,proto3" json:"server_name_to_verify,omitempty"`
|
// @Document Replaces server_name to verify the peer cert.
|
||||||
|
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||||
|
// @Critical
|
||||||
|
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -353,11 +354,11 @@ func (x *Config) GetCurvePreferences() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetServerNameToVerify() string {
|
func (x *Config) GetVerifyPeerCertInNames() []string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.ServerNameToVerify
|
return x.VerifyPeerCertInNames
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||||
@@ -391,7 +392,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
||||||
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
||||||
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
||||||
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x93, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x9a, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
||||||
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
||||||
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
||||||
@@ -437,18 +438,19 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12, 0x2b,
|
0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12, 0x2b,
|
||||||
0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
||||||
0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76, 0x65,
|
0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76, 0x65,
|
||||||
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x73,
|
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x76,
|
||||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x76, 0x65,
|
0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
|
||||||
0x72, 0x69, 0x66, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76,
|
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15,
|
||||||
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x6f, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x73,
|
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
|
||||||
0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c,
|
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||||
0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
||||||
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74,
|
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
||||||
0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
|
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58,
|
||||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
|
||||||
0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -69,16 +69,14 @@ message Config {
|
|||||||
|
|
||||||
bool reject_unknown_sni = 12;
|
bool reject_unknown_sni = 12;
|
||||||
|
|
||||||
/* @Document A pinned certificate chain sha256 hash.
|
/* @Document Some certificate chain sha256 hashes.
|
||||||
@Document If the server's hash does not match this value, the connection will be aborted.
|
@Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
|
||||||
@Document This value replace allow_insecure.
|
|
||||||
@Critical
|
@Critical
|
||||||
*/
|
*/
|
||||||
repeated bytes pinned_peer_certificate_chain_sha256 = 13;
|
repeated bytes pinned_peer_certificate_chain_sha256 = 13;
|
||||||
|
|
||||||
/* @Document A pinned certificate public key sha256 hash.
|
/* @Document Some certificate public key sha256 hashes.
|
||||||
@Document If the server's public key hash does not match this value, the connection will be aborted.
|
@Document After normal validation (required), if one of certs in verified chain matches one of these values, the connection will be eventually accepted.
|
||||||
@Document This value replace allow_insecure.
|
|
||||||
@Critical
|
@Critical
|
||||||
*/
|
*/
|
||||||
repeated bytes pinned_peer_certificate_public_key_sha256 = 14;
|
repeated bytes pinned_peer_certificate_public_key_sha256 = 14;
|
||||||
@@ -88,5 +86,9 @@ message Config {
|
|||||||
// Lists of string as CurvePreferences values.
|
// Lists of string as CurvePreferences values.
|
||||||
repeated string curve_preferences = 16;
|
repeated string curve_preferences = 16;
|
||||||
|
|
||||||
string server_name_to_verify = 17;
|
/* @Document Replaces server_name to verify the peer cert.
|
||||||
|
@Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||||
|
@Critical
|
||||||
|
*/
|
||||||
|
repeated string verify_peer_cert_in_names = 17;
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ type Interface interface {
|
|||||||
net.Conn
|
net.Conn
|
||||||
HandshakeContext(ctx context.Context) error
|
HandshakeContext(ctx context.Context) error
|
||||||
VerifyHostname(host string) error
|
VerifyHostname(host string) error
|
||||||
|
HandshakeContextServerName(ctx context.Context) string
|
||||||
NegotiatedProtocol() string
|
NegotiatedProtocol() string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,15 +44,11 @@ func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) HandshakeAddressContext(ctx context.Context) net.Address {
|
func (c *Conn) HandshakeContextServerName(ctx context.Context) string {
|
||||||
if err := c.HandshakeContext(ctx); err != nil {
|
if err := c.HandshakeContext(ctx); err != nil {
|
||||||
return nil
|
return ""
|
||||||
}
|
}
|
||||||
state := c.ConnectionState()
|
return c.ConnectionState().ServerName
|
||||||
if state.ServerName == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return net.ParseAddress(state.ServerName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) NegotiatedProtocol() string {
|
func (c *Conn) NegotiatedProtocol() string {
|
||||||
@@ -85,15 +82,11 @@ func (c *UConn) Close() error {
|
|||||||
return c.Conn.Close()
|
return c.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UConn) HandshakeAddressContext(ctx context.Context) net.Address {
|
func (c *UConn) HandshakeContextServerName(ctx context.Context) string {
|
||||||
if err := c.HandshakeContext(ctx); err != nil {
|
if err := c.HandshakeContext(ctx); err != nil {
|
||||||
return nil
|
return ""
|
||||||
}
|
}
|
||||||
state := c.ConnectionState()
|
return c.ConnectionState().ServerName
|
||||||
if state.ServerName == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return net.ParseAddress(state.ServerName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebsocketHandshake basically calls UConn.Handshake inside it but it will only send
|
// WebsocketHandshake basically calls UConn.Handshake inside it but it will only send
|
||||||
@@ -134,17 +127,13 @@ func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) ne
|
|||||||
}
|
}
|
||||||
|
|
||||||
func copyConfig(c *tls.Config) *utls.Config {
|
func copyConfig(c *tls.Config) *utls.Config {
|
||||||
serverNameToVerify := ""
|
|
||||||
if r, ok := c.Rand.(*RandCarrier); ok {
|
|
||||||
serverNameToVerify = r.ServerNameToVerify
|
|
||||||
}
|
|
||||||
return &utls.Config{
|
return &utls.Config{
|
||||||
|
Rand: c.Rand,
|
||||||
RootCAs: c.RootCAs,
|
RootCAs: c.RootCAs,
|
||||||
ServerName: c.ServerName,
|
ServerName: c.ServerName,
|
||||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||||
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
||||||
KeyLogWriter: c.KeyLogWriter,
|
KeyLogWriter: c.KeyLogWriter,
|
||||||
InsecureServerNameToVerify: serverNameToVerify,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,10 +151,14 @@ func init() {
|
|||||||
weights := utls.DefaultWeights
|
weights := utls.DefaultWeights
|
||||||
weights.TLSVersMax_Set_VersionTLS13 = 1
|
weights.TLSVersMax_Set_VersionTLS13 = 1
|
||||||
weights.FirstKeyShare_Set_CurveP256 = 0
|
weights.FirstKeyShare_Set_CurveP256 = 0
|
||||||
randomized := utls.HelloRandomized
|
randomized := utls.HelloRandomizedALPN
|
||||||
randomized.Seed, _ = utls.NewPRNGSeed()
|
randomized.Seed, _ = utls.NewPRNGSeed()
|
||||||
randomized.Weights = &weights
|
randomized.Weights = &weights
|
||||||
|
randomizednoalpn := utls.HelloRandomizedNoALPN
|
||||||
|
randomizednoalpn.Seed, _ = utls.NewPRNGSeed()
|
||||||
|
randomizednoalpn.Weights = &weights
|
||||||
PresetFingerprints["randomized"] = &randomized
|
PresetFingerprints["randomized"] = &randomized
|
||||||
|
PresetFingerprints["randomizednoalpn"] = &randomizednoalpn
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) {
|
func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) {
|
||||||
@@ -196,6 +189,7 @@ var PresetFingerprints = map[string]*utls.ClientHelloID{
|
|||||||
"qq": &utls.HelloQQ_Auto,
|
"qq": &utls.HelloQQ_Auto,
|
||||||
"random": nil,
|
"random": nil,
|
||||||
"randomized": nil,
|
"randomized": nil,
|
||||||
|
"randomizednoalpn": nil,
|
||||||
"unsafe": nil,
|
"unsafe": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,12 +198,14 @@ var ModernFingerprints = map[string]*utls.ClientHelloID{
|
|||||||
"hellofirefox_99": &utls.HelloFirefox_99,
|
"hellofirefox_99": &utls.HelloFirefox_99,
|
||||||
"hellofirefox_102": &utls.HelloFirefox_102,
|
"hellofirefox_102": &utls.HelloFirefox_102,
|
||||||
"hellofirefox_105": &utls.HelloFirefox_105,
|
"hellofirefox_105": &utls.HelloFirefox_105,
|
||||||
|
"hellofirefox_120": &utls.HelloFirefox_120,
|
||||||
"hellochrome_83": &utls.HelloChrome_83,
|
"hellochrome_83": &utls.HelloChrome_83,
|
||||||
"hellochrome_87": &utls.HelloChrome_87,
|
"hellochrome_87": &utls.HelloChrome_87,
|
||||||
"hellochrome_96": &utls.HelloChrome_96,
|
"hellochrome_96": &utls.HelloChrome_96,
|
||||||
"hellochrome_100": &utls.HelloChrome_100,
|
"hellochrome_100": &utls.HelloChrome_100,
|
||||||
"hellochrome_102": &utls.HelloChrome_102,
|
"hellochrome_102": &utls.HelloChrome_102,
|
||||||
"hellochrome_106_shuffle": &utls.HelloChrome_106_Shuffle,
|
"hellochrome_106_shuffle": &utls.HelloChrome_106_Shuffle,
|
||||||
|
"hellochrome_120": &utls.HelloChrome_120,
|
||||||
"helloios_13": &utls.HelloIOS_13,
|
"helloios_13": &utls.HelloIOS_13,
|
||||||
"helloios_14": &utls.HelloIOS_14,
|
"helloios_14": &utls.HelloIOS_14,
|
||||||
"helloedge_85": &utls.HelloEdge_85,
|
"helloedge_85": &utls.HelloEdge_85,
|
||||||
|
Reference in New Issue
Block a user