mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 09:36:49 +08:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 | ||
![]() |
0a8470cb14 | ||
![]() |
efdc70fbf7 | ||
![]() |
f35fb08aeb | ||
![]() |
1bb0beaa43 | ||
![]() |
03131c72db | ||
![]() |
7b59379d73 | ||
![]() |
a7a83624c5 | ||
![]() |
3a7a78ff3a | ||
![]() |
5679d717ee | ||
![]() |
740a6b0dcd | ||
![]() |
2522cfd7be | ||
![]() |
a0822cb440 | ||
![]() |
ca9a902213 | ||
![]() |
f4fd8b8fad | ||
![]() |
14a6636a41 | ||
![]() |
30cb22afb1 | ||
![]() |
66dd7808b6 | ||
![]() |
f1ff454e67 | ||
![]() |
4576f56ec8 | ||
![]() |
9b1855f719 | ||
![]() |
3e590a4eb1 | ||
![]() |
ef4a3c1cae | ||
![]() |
5635254ebc | ||
![]() |
ce6c0dc690 | ||
![]() |
aeb12d9e3b | ||
![]() |
de53a3b94e | ||
![]() |
2f52aa7ed8 | ||
![]() |
ca50c9cbe6 | ||
![]() |
33186ca5e6 |
73
.github/workflows/release-win7.yml
vendored
73
.github/workflows/release-win7.yml
vendored
@@ -1,11 +1,5 @@
|
||||
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:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
@@ -15,51 +9,7 @@ on:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Restore Cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-
|
||||
|
||||
- name: Update Geodat
|
||||
id: update
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 60
|
||||
retry_wait_seconds: 60
|
||||
max_attempts: 60
|
||||
command: |
|
||||
[ -d 'resources' ] || mkdir resources
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
||||
FILE_NAME="${INFO[2]}.dat"
|
||||
echo -e "Verifying HASH key..."
|
||||
HASH="$(curl -sL "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
|
||||
continue
|
||||
else
|
||||
echo -e "Downloading https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat..."
|
||||
curl -L "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat" -o ./resources/${FILE_NAME}
|
||||
echo -e "Verifying HASH key..."
|
||||
[ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
|
||||
echo "unhit=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Save Cache
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ steps.update.outputs.unhit }}
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
build:
|
||||
needs: prepare
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
@@ -81,6 +31,9 @@ jobs:
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Show workflow information
|
||||
run: |
|
||||
_NAME=${{ matrix.assetname }}
|
||||
@@ -90,18 +43,17 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
|
||||
- name: Setup patched builder
|
||||
run: |
|
||||
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/*
|
||||
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
|
||||
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v4
|
||||
rm ./go-for-win7-linux-amd64.zip
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
@@ -109,10 +61,15 @@ jobs:
|
||||
- name: Build Xray
|
||||
run: |
|
||||
mkdir -p build_assets
|
||||
make
|
||||
find . -maxdepth 1 -type f -regex './\(wxray\|xray\).exe' -exec mv {} ./build_assets/ \;
|
||||
COMMID=$(git describe --always --dirty)
|
||||
echo 'Building Xray for Windows 7...'
|
||||
go build -o build_assets/xray.exe -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||
echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs
|
||||
echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1
|
||||
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
|
||||
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||
|
||||
- name: Restore Cache
|
||||
- name: Restore Geodat Cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: resources
|
||||
|
70
.github/workflows/release.yml
vendored
70
.github/workflows/release.yml
vendored
@@ -1,11 +1,5 @@
|
||||
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:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
@@ -15,51 +9,7 @@ on:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Restore Cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-
|
||||
|
||||
- name: Update Geodat
|
||||
id: update
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 60
|
||||
retry_wait_seconds: 60
|
||||
max_attempts: 60
|
||||
command: |
|
||||
[ -d 'resources' ] || mkdir resources
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
||||
FILE_NAME="${INFO[2]}.dat"
|
||||
echo -e "Verifying HASH key..."
|
||||
HASH="$(curl -sL "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
|
||||
continue
|
||||
else
|
||||
echo -e "Downloading https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat..."
|
||||
curl -L "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat" -o ./resources/${FILE_NAME}
|
||||
echo -e "Verifying HASH key..."
|
||||
[ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
|
||||
echo "unhit=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Save Cache
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ steps.update.outputs.unhit }}
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
build:
|
||||
needs: prepare
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
@@ -173,10 +123,24 @@ jobs:
|
||||
- name: Build Xray
|
||||
run: |
|
||||
mkdir -p build_assets
|
||||
make
|
||||
find . -maxdepth 1 -type f -regex './\(wxray\|xray\|xray_softfloat\)\(\|.exe\)' -exec mv {} ./build_assets/ \;
|
||||
COMMID=$(git describe --always --dirty)
|
||||
if [[ ${GOOS} == 'windows' ]]; then
|
||||
echo 'Building Xray for Windows...'
|
||||
go build -o build_assets/xray.exe -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||
echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs
|
||||
echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1
|
||||
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
|
||||
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||
else
|
||||
echo 'Building Xray...'
|
||||
go build -o build_assets/xray -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||
if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then
|
||||
echo 'Building soft-float Xray for MIPS/MIPSLE 32-bit...'
|
||||
GOMIPS=softfloat go build -o build_assets/xray_softfloat -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Restore Cache
|
||||
- name: Restore Geodat Cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: resources
|
||||
|
65
.github/workflows/scheduled-assets-update.yml
vendored
Normal file
65
.github/workflows/scheduled-assets-update.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Scheduled assets update
|
||||
|
||||
# NOTE: This Github Actions is required by other actions, for preparing other packaging assets in a
|
||||
# routine manner, for example: GeoIP/GeoSite.
|
||||
# Currently updating:
|
||||
# - Geodat (GeoIP/Geosite)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Update GeoData on every day (22:30 UTC)
|
||||
- cron: '30 22 * * *'
|
||||
push:
|
||||
# Prevent triggering update request storm
|
||||
paths:
|
||||
- ".github/workflows/scheduled-assets-update.yml"
|
||||
pull_request:
|
||||
# Prevent triggering update request storm
|
||||
paths:
|
||||
- ".github/workflows/scheduled-assets-update.yml"
|
||||
|
||||
jobs:
|
||||
geodat:
|
||||
if: github.event.schedule == '30 22 * * *' || github.event_name == 'push'|| github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Restore Geodat Cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-
|
||||
|
||||
- name: Update Geodat
|
||||
id: update
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 60
|
||||
retry_wait_seconds: 60
|
||||
max_attempts: 60
|
||||
command: |
|
||||
[ -d 'resources' ] || mkdir resources
|
||||
LIST=('Loyalsoldier v2ray-rules-dat geoip geoip' 'Loyalsoldier v2ray-rules-dat geosite geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3,$4}'))
|
||||
FILE_NAME="${INFO[3]}.dat"
|
||||
echo -e "Verifying HASH key..."
|
||||
HASH="$(curl -sL "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
|
||||
continue
|
||||
else
|
||||
echo -e "Downloading https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat..."
|
||||
curl -L "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat" -o ./resources/${FILE_NAME}
|
||||
echo -e "Verifying HASH key..."
|
||||
[ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
|
||||
echo "unhit=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Save Geodat Cache
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ steps.update.outputs.unhit }}
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-${{ github.sha }}-${{ github.run_number }}
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- name: Restore Cache
|
||||
- name: Restore Geodat Cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: resources
|
||||
|
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 VLESS](https://t.me/projectVless) (non-Chinese)
|
||||
[Project VLESS](https://t.me/projectVless) (Русский)
|
||||
|
||||
[Project XHTTP](https://t.me/projectXhttp) (Persian)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -72,6 +74,8 @@
|
||||
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
|
||||
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
|
||||
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
||||
- Asuswrt-Merlin
|
||||
- [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui)
|
||||
- Windows
|
||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||
- [Furious](https://github.com/LorenEteval/Furious)
|
||||
@@ -98,6 +102,7 @@
|
||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||
- Xray Tools
|
||||
- [xray-knife](https://github.com/lilendian0x00/xray-knife)
|
||||
- [xray-checker](https://github.com/kutovoys/xray-checker)
|
||||
- Xray Wrapper
|
||||
- [XTLS/libXray](https://github.com/XTLS/libXray)
|
||||
- [xtlsapi](https://github.com/hiddify/xtlsapi)
|
||||
@@ -121,25 +126,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).
|
||||
- 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)
|
||||
|
||||
```powershell
|
||||
$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
|
||||
|
||||
```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
|
||||
|
||||
Make sure that you are using the same Go version, and remember to set the git commit id (7 bytes):
|
||||
|
||||
```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
|
||||
|
@@ -43,10 +43,12 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
|
||||
}
|
||||
switch {
|
||||
case strings.EqualFold(u.String(), "localhost"):
|
||||
return NewLocalNameServer(), nil
|
||||
case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
|
||||
return NewDoHNameServer(u, dispatcher, queryStrategy)
|
||||
case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
|
||||
return NewLocalNameServer(queryStrategy), nil
|
||||
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
|
||||
return NewDoHNameServer(u, dispatcher, queryStrategy, false)
|
||||
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
|
||||
return NewDoHNameServer(u, dispatcher, queryStrategy, true)
|
||||
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
|
||||
return NewDoHLocalNameServer(u, queryStrategy), nil
|
||||
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
|
||||
return NewQUICNameServer(u, queryStrategy)
|
||||
|
@@ -3,6 +3,7 @@ package dns
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format,
|
||||
@@ -41,49 +43,64 @@ type DoHNameServer struct {
|
||||
}
|
||||
|
||||
// NewDoHNameServer creates DOH server object for remote resolving.
|
||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy QueryStrategy) (*DoHNameServer, error) {
|
||||
errors.LogInfo(context.Background(), "DNS: created Remote DOH client for ", url.String())
|
||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy QueryStrategy, h2c bool) (*DoHNameServer, error) {
|
||||
url.Scheme = "https"
|
||||
errors.LogInfo(context.Background(), "DNS: created Remote DNS-over-HTTPS client for ", url.String(), ", with h2c ", h2c)
|
||||
s := baseDOHNameServer(url, "DOH", queryStrategy)
|
||||
|
||||
s.dispatcher = dispatcher
|
||||
tr := &http.Transport{
|
||||
MaxIdleConns: 30,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link, err := s.dispatcher.Dispatch(toDnsContext(ctx, s.dohURL), dest)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dnsCtx := toDnsContext(ctx, s.dohURL)
|
||||
if h2c {
|
||||
dnsCtx = session.ContextWithMitmAlpn11(dnsCtx, false) // for insurance
|
||||
dnsCtx = session.ContextWithMitmServerName(dnsCtx, url.Hostname())
|
||||
}
|
||||
link, err := s.dispatcher.Dispatch(dnsCtx, dest)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := common.ChainedClosable{}
|
||||
if cw, ok := link.Writer.(common.Closable); ok {
|
||||
cc = append(cc, cw)
|
||||
}
|
||||
if cr, ok := link.Reader.(common.Closable); ok {
|
||||
cc = append(cc, cr)
|
||||
}
|
||||
return cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(link.Writer),
|
||||
cnc.ConnectionOutputMulti(link.Reader),
|
||||
cnc.ConnectionOnClose(cc),
|
||||
), nil
|
||||
cc := common.ChainedClosable{}
|
||||
if cw, ok := link.Writer.(common.Closable); ok {
|
||||
cc = append(cc, cw)
|
||||
}
|
||||
if cr, ok := link.Reader.(common.Closable); ok {
|
||||
cc = append(cc, cr)
|
||||
}
|
||||
return cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(link.Writer),
|
||||
cnc.ConnectionOutputMulti(link.Reader),
|
||||
cnc.ConnectionOnClose(cc),
|
||||
), nil
|
||||
}
|
||||
|
||||
s.httpClient = &http.Client{
|
||||
Timeout: time.Second * 180,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 30,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: dialContext,
|
||||
},
|
||||
}
|
||||
s.httpClient = &http.Client{
|
||||
Timeout: time.Second * 180,
|
||||
Transport: tr,
|
||||
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
|
||||
@@ -118,7 +135,7 @@ func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy) *DoHNameSe
|
||||
Timeout: time.Second * 180,
|
||||
Transport: tr,
|
||||
}
|
||||
errors.LogInfo(context.Background(), "DNS: created Local DOH client for ", url.String())
|
||||
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-HTTPS client for ", url.String())
|
||||
return s
|
||||
}
|
||||
|
||||
|
@@ -14,13 +14,19 @@ import (
|
||||
|
||||
// LocalNameServer is an wrapper over local DNS feature.
|
||||
type LocalNameServer struct {
|
||||
client *localdns.Client
|
||||
client *localdns.Client
|
||||
queryStrategy QueryStrategy
|
||||
}
|
||||
|
||||
const errEmptyResponse = "No address associated with hostname"
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) {
|
||||
option = ResolveIpOptionOverride(s.queryStrategy, option)
|
||||
if !option.IPv4Enable && !option.IPv6Enable {
|
||||
return nil, dns.ErrEmptyResponse
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ips, err = s.client.LookupIP(domain, option)
|
||||
|
||||
@@ -42,14 +48,15 @@ func (s *LocalNameServer) Name() string {
|
||||
}
|
||||
|
||||
// NewLocalNameServer creates localdns server object for directly lookup in system DNS.
|
||||
func NewLocalNameServer() *LocalNameServer {
|
||||
func NewLocalNameServer(queryStrategy QueryStrategy) *LocalNameServer {
|
||||
errors.LogInfo(context.Background(), "DNS: created localhost client")
|
||||
return &LocalNameServer{
|
||||
client: localdns.New(),
|
||||
queryStrategy: queryStrategy,
|
||||
client: localdns.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewLocalDNSClient creates localdns client object for directly lookup in system DNS.
|
||||
func NewLocalDNSClient() *Client {
|
||||
return &Client{server: NewLocalNameServer()}
|
||||
return &Client{server: NewLocalNameServer(QueryStrategy_USE_IP)}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestLocalNameServer(t *testing.T) {
|
||||
s := NewLocalNameServer()
|
||||
s := NewLocalNameServer(QueryStrategy_USE_IP)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/quic-go"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
|
@@ -27,7 +27,8 @@ type Config struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Tag of the outbound handler that handles metrics http connections.
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
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() {
|
||||
@@ -67,20 +68,28 @@ func (x *Config) GetTag() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetListen() string {
|
||||
if x != nil {
|
||||
return x.Listen
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_app_metrics_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_app_metrics_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 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,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
|
||||
0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
|
||||
0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74,
|
||||
0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||
0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
|
||||
0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74,
|
||||
0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -10,4 +10,5 @@ option java_multiple_files = true;
|
||||
message Config {
|
||||
// Tag of the outbound handler that handles metrics http connections.
|
||||
string tag = 1;
|
||||
string listen = 2;
|
||||
}
|
||||
|
@@ -24,12 +24,15 @@ type MetricsHandler struct {
|
||||
statsManager feature_stats.Manager
|
||||
observatory extension.Observatory
|
||||
tag string
|
||||
listen string
|
||||
tcpListener net.Listener
|
||||
}
|
||||
|
||||
// NewMetricsHandler creates a new MetricsHandler based on the given config.
|
||||
func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) {
|
||||
c := &MetricsHandler{
|
||||
tag: config.Tag,
|
||||
tag: config.Tag,
|
||||
listen: config.Listen,
|
||||
}
|
||||
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) {
|
||||
c.statsManager = sm
|
||||
@@ -87,6 +90,23 @@ func (p *MetricsHandler) Type() interface{} {
|
||||
}
|
||||
|
||||
func (p *MetricsHandler) Start() error {
|
||||
|
||||
// direct listen a port if listen is set
|
||||
if p.listen != "" {
|
||||
TCPlistener, err := net.Listen("tcp", p.listen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.tcpListener = TCPlistener
|
||||
errors.LogInfo(context.Background(), "Metrics server listening on ", p.listen)
|
||||
|
||||
go func() {
|
||||
if err := http.Serve(TCPlistener, http.DefaultServeMux); err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
listener := &OutboundListener{
|
||||
buffer: make(chan net.Conn, 4),
|
||||
done: done.New(),
|
||||
|
@@ -66,10 +66,10 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
|
||||
settings.Timeout = time.Duration(5) * time.Second
|
||||
}
|
||||
return &HealthPing{
|
||||
ctx: ctx,
|
||||
ctx: ctx,
|
||||
dispatcher: dispatcher,
|
||||
Settings: settings,
|
||||
Results: nil,
|
||||
Settings: settings,
|
||||
Results: nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -32,7 +32,7 @@ type Observer struct {
|
||||
|
||||
finished *done.Instance
|
||||
|
||||
ohm outbound.Manager
|
||||
ohm outbound.Manager
|
||||
dispatcher routing.Dispatcher
|
||||
}
|
||||
|
||||
@@ -226,9 +226,9 @@ func New(ctx context.Context, config *Config) (*Observer, error) {
|
||||
return nil, errors.New("Cannot get depended features").Base(err)
|
||||
}
|
||||
return &Observer{
|
||||
config: config,
|
||||
ctx: ctx,
|
||||
ohm: outboundManager,
|
||||
config: config,
|
||||
ctx: ctx,
|
||||
ohm: outboundManager,
|
||||
dispatcher: dispatcher,
|
||||
}, nil
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ type DynamicInboundHandler struct {
|
||||
receiverConfig *proxyman.ReceiverConfig
|
||||
streamSettings *internet.MemoryStreamConfig
|
||||
portMutex sync.Mutex
|
||||
portsInUse map[net.Port]bool
|
||||
portsInUse map[net.Port]struct{}
|
||||
workerMutex sync.RWMutex
|
||||
worker []worker
|
||||
lastRefresh time.Time
|
||||
@@ -39,7 +39,7 @@ func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *p
|
||||
tag: tag,
|
||||
proxyConfig: proxyConfig,
|
||||
receiverConfig: receiverConfig,
|
||||
portsInUse: make(map[net.Port]bool),
|
||||
portsInUse: make(map[net.Port]struct{}),
|
||||
mux: mux.NewServer(ctx),
|
||||
v: v,
|
||||
ctx: ctx,
|
||||
@@ -84,7 +84,7 @@ func (h *DynamicInboundHandler) allocatePort() net.Port {
|
||||
port := net.Port(allPorts[r])
|
||||
_, used := h.portsInUse[port]
|
||||
if !used {
|
||||
h.portsInUse[port] = true
|
||||
h.portsInUse[port] = struct{}{}
|
||||
return port
|
||||
}
|
||||
}
|
||||
|
@@ -464,8 +464,7 @@ func (w *dsWorker) callback(conn stat.Connection) {
|
||||
}
|
||||
}
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
// Unix have no source addr, so we use gateway as source for log.
|
||||
Source: net.UnixDestination(w.address),
|
||||
Source: net.DestinationFromAddr(conn.RemoteAddr()),
|
||||
Gateway: net.UnixDestination(w.address),
|
||||
Tag: w.tag,
|
||||
Conn: conn,
|
||||
|
@@ -60,6 +60,24 @@ func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsReque
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) {
|
||||
c := s.stats.GetOnlineMap(request.Name)
|
||||
|
||||
if c == nil {
|
||||
return nil, errors.New(request.Name, " not found.")
|
||||
}
|
||||
|
||||
ips := make(map[string]int64)
|
||||
for ip, t := range c.IpTimeMap() {
|
||||
ips[ip] = t.Unix()
|
||||
}
|
||||
|
||||
return &GetStatsOnlineIpListResponse{
|
||||
Name: request.Name,
|
||||
Ips: ips,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
|
||||
matcher, err := strmatcher.Substr.New(request.Pattern)
|
||||
if err != nil {
|
||||
|
@@ -424,6 +424,59 @@ func (x *SysStatsResponse) GetUptime() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetStatsOnlineIpListResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Ips map[string]int64 `protobuf:"bytes,2,rep,name=ips,proto3" json:"ips,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (x *GetStatsOnlineIpListResponse) Reset() {
|
||||
*x = GetStatsOnlineIpListResponse{}
|
||||
mi := &file_app_stats_command_command_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetStatsOnlineIpListResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetStatsOnlineIpListResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetStatsOnlineIpListResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_stats_command_command_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetStatsOnlineIpListResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetStatsOnlineIpListResponse) Descriptor() ([]byte, []int) {
|
||||
return file_app_stats_command_command_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *GetStatsOnlineIpListResponse) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetStatsOnlineIpListResponse) GetIps() map[string]int64 {
|
||||
if x != nil {
|
||||
return x.Ips
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -432,7 +485,7 @@ type Config struct {
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*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.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -444,7 +497,7 @@ func (x *Config) String() string {
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
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 {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -457,7 +510,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_app_stats_command_command_proto_rawDescGZIP(), []int{7}
|
||||
return file_app_stats_command_command_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
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,
|
||||
0x61, 0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55,
|
||||
0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x70, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xa1, 0x03,
|
||||
0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f,
|
||||
0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73,
|
||||
0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74,
|
||||
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
||||
0x65, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e,
|
||||
0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61,
|
||||
0x69, 0x6d, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73,
|
||||
0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18,
|
||||
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x70, 0x73, 0x45,
|
||||
0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x69, 0x70, 0x73, 0x1a, 0x36, 0x0a, 0x08, 0x49, 0x70, 0x73,
|
||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||
0x01, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0x9a, 0x04, 0x0a, 0x0c,
|
||||
0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08,
|
||||
0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
|
||||
0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61,
|
||||
0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74,
|
||||
0x61, 0x74, 0x73, 0x52, 0x65, 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,
|
||||
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, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53,
|
||||
0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50,
|
||||
0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70,
|
||||
0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02,
|
||||
0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,
|
||||
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73,
|
||||
0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73,
|
||||
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
||||
0x77, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e,
|
||||
0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
||||
0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
|
||||
0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x34, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74,
|
||||
0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
|
||||
0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
|
||||
0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -554,33 +627,38 @@ func file_app_stats_command_command_proto_rawDescGZIP() []byte {
|
||||
return file_app_stats_command_command_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_app_stats_command_command_proto_goTypes = []any{
|
||||
(*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest
|
||||
(*Stat)(nil), // 1: xray.app.stats.command.Stat
|
||||
(*GetStatsResponse)(nil), // 2: xray.app.stats.command.GetStatsResponse
|
||||
(*QueryStatsRequest)(nil), // 3: xray.app.stats.command.QueryStatsRequest
|
||||
(*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse
|
||||
(*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest
|
||||
(*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse
|
||||
(*Config)(nil), // 7: xray.app.stats.command.Config
|
||||
(*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest
|
||||
(*Stat)(nil), // 1: xray.app.stats.command.Stat
|
||||
(*GetStatsResponse)(nil), // 2: xray.app.stats.command.GetStatsResponse
|
||||
(*QueryStatsRequest)(nil), // 3: xray.app.stats.command.QueryStatsRequest
|
||||
(*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse
|
||||
(*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest
|
||||
(*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse
|
||||
(*GetStatsOnlineIpListResponse)(nil), // 7: xray.app.stats.command.GetStatsOnlineIpListResponse
|
||||
(*Config)(nil), // 8: xray.app.stats.command.Config
|
||||
nil, // 9: xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry
|
||||
}
|
||||
var file_app_stats_command_command_proto_depIdxs = []int32{
|
||||
1, // 0: xray.app.stats.command.GetStatsResponse.stat:type_name -> xray.app.stats.command.Stat
|
||||
1, // 1: xray.app.stats.command.QueryStatsResponse.stat:type_name -> xray.app.stats.command.Stat
|
||||
0, // 2: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest
|
||||
0, // 3: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest
|
||||
3, // 4: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest
|
||||
5, // 5: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest
|
||||
2, // 6: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse
|
||||
2, // 7: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse
|
||||
4, // 8: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse
|
||||
6, // 9: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse
|
||||
6, // [6:10] is the sub-list for method output_type
|
||||
2, // [2:6] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
9, // 2: xray.app.stats.command.GetStatsOnlineIpListResponse.ips:type_name -> xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry
|
||||
0, // 3: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest
|
||||
0, // 4: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest
|
||||
3, // 5: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest
|
||||
5, // 6: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest
|
||||
0, // 7: xray.app.stats.command.StatsService.GetStatsOnlineIpList:input_type -> xray.app.stats.command.GetStatsRequest
|
||||
2, // 8: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse
|
||||
2, // 9: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse
|
||||
4, // 10: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse
|
||||
6, // 11: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse
|
||||
7, // 12: xray.app.stats.command.StatsService.GetStatsOnlineIpList:output_type -> xray.app.stats.command.GetStatsOnlineIpListResponse
|
||||
8, // [8:13] is the sub-list for method output_type
|
||||
3, // [3:8] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
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(),
|
||||
RawDescriptor: file_app_stats_command_command_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 8,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@@ -46,11 +46,17 @@ message SysStatsResponse {
|
||||
uint32 Uptime = 10;
|
||||
}
|
||||
|
||||
message GetStatsOnlineIpListResponse {
|
||||
string name = 1;
|
||||
map<string, int64> ips = 2;
|
||||
}
|
||||
|
||||
service StatsService {
|
||||
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
|
||||
rpc GetStatsOnline(GetStatsRequest) returns (GetStatsResponse) {}
|
||||
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
|
||||
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
|
||||
rpc GetStatsOnlineIpList(GetStatsRequest) returns (GetStatsOnlineIpListResponse) {}
|
||||
}
|
||||
|
||||
message Config {}
|
||||
|
@@ -19,10 +19,11 @@ import (
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
StatsService_GetStats_FullMethodName = "/xray.app.stats.command.StatsService/GetStats"
|
||||
StatsService_GetStatsOnline_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnline"
|
||||
StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats"
|
||||
StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats"
|
||||
StatsService_GetStats_FullMethodName = "/xray.app.stats.command.StatsService/GetStats"
|
||||
StatsService_GetStatsOnline_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnline"
|
||||
StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats"
|
||||
StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats"
|
||||
StatsService_GetStatsOnlineIpList_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnlineIpList"
|
||||
)
|
||||
|
||||
// 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)
|
||||
QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error)
|
||||
GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error)
|
||||
GetStatsOnlineIpList(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsOnlineIpListResponse, error)
|
||||
}
|
||||
|
||||
type statsServiceClient struct {
|
||||
@@ -83,6 +85,16 @@ func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsReques
|
||||
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.
|
||||
// All implementations must embed UnimplementedStatsServiceServer
|
||||
// for forward compatibility.
|
||||
@@ -91,6 +103,7 @@ type StatsServiceServer interface {
|
||||
GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
||||
QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error)
|
||||
GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error)
|
||||
GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error)
|
||||
mustEmbedUnimplementedStatsServiceServer()
|
||||
}
|
||||
|
||||
@@ -113,6 +126,9 @@ func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRe
|
||||
func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
|
||||
}
|
||||
func (UnimplementedStatsServiceServer) GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStatsOnlineIpList not implemented")
|
||||
}
|
||||
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
|
||||
func (UnimplementedStatsServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -206,6 +222,24 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StatsService_GetStatsOnlineIpList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetStatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: StatsService_GetStatsOnlineIpList_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, req.(*GetStatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -229,6 +263,10 @@ var StatsService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "GetSysStats",
|
||||
Handler: _StatsService_GetSysStats_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStatsOnlineIpList",
|
||||
Handler: _StatsService_GetStatsOnlineIpList_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "app/stats/command/command.proto",
|
||||
|
@@ -35,37 +35,56 @@ func (c *OnlineMap) List() []string {
|
||||
|
||||
// AddIP implements stats.OnlineMap.
|
||||
func (c *OnlineMap) AddIP(ip string) {
|
||||
list := c.ipList
|
||||
|
||||
if ip == "127.0.0.1" {
|
||||
return
|
||||
}
|
||||
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
|
||||
if _, ok := c.ipList[ip]; !ok {
|
||||
c.ipList[ip] = time.Now()
|
||||
if _, ok := list[ip]; !ok {
|
||||
c.access.Lock()
|
||||
list[ip] = time.Now()
|
||||
c.access.Unlock()
|
||||
}
|
||||
if time.Since(c.lastCleanup) > c.cleanupPeriod {
|
||||
now := time.Now()
|
||||
for k, t := range c.ipList {
|
||||
diff := now.Sub(t)
|
||||
if diff.Seconds() > 20 {
|
||||
delete(c.ipList, k) // safe to do delete in range
|
||||
}
|
||||
}
|
||||
list = c.RemoveExpiredIPs(list)
|
||||
c.lastCleanup = time.Now()
|
||||
}
|
||||
|
||||
c.value = len(c.ipList)
|
||||
c.value = len(list)
|
||||
c.ipList = list
|
||||
}
|
||||
|
||||
func (c *OnlineMap) GetKeys() []string {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
c.access.RLock()
|
||||
defer c.access.RUnlock()
|
||||
|
||||
keys := []string{}
|
||||
for k := range c.ipList {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OnlineMap) RemoveExpiredIPs(list map[string]time.Time) map[string]time.Time {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for k, t := range list {
|
||||
diff := now.Sub(t)
|
||||
if diff.Seconds() > 20 {
|
||||
delete(list, k)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (c *OnlineMap) IpTimeMap() map[string]time.Time {
|
||||
list := c.ipList
|
||||
if time.Since(c.lastCleanup) > c.cleanupPeriod {
|
||||
list = c.RemoveExpiredIPs(list)
|
||||
c.lastCleanup = time.Now()
|
||||
}
|
||||
|
||||
return c.ipList
|
||||
}
|
||||
|
@@ -146,7 +146,7 @@ func (w *fileLogWriter) Close() error {
|
||||
func CreateStdoutLogWriter() WriterCreator {
|
||||
return func() Writer {
|
||||
return &consoleLogWriter{
|
||||
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lmicroseconds),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func CreateStdoutLogWriter() WriterCreator {
|
||||
func CreateStderrLogWriter() WriterCreator {
|
||||
return func() Writer {
|
||||
return &consoleLogWriter{
|
||||
logger: log.New(os.Stderr, "", log.Ldate|log.Ltime),
|
||||
logger: log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lmicroseconds),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func CreateFileLogWriter(path string) (WriterCreator, error) {
|
||||
}
|
||||
return &fileLogWriter{
|
||||
file: file,
|
||||
logger: log.New(file, "", log.Ldate|log.Ltime),
|
||||
logger: log.New(file, "", log.Ldate|log.Ltime|log.Lmicroseconds),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
@@ -89,12 +89,10 @@ func UnixDestination(address Address) Destination {
|
||||
// NetAddr returns the network address in this Destination in string form.
|
||||
func (d Destination) NetAddr() string {
|
||||
addr := ""
|
||||
if d.Address != nil {
|
||||
if d.Network == Network_TCP || d.Network == Network_UDP {
|
||||
addr = d.Address.String() + ":" + d.Port.String()
|
||||
} else if d.Network == Network_UNIX {
|
||||
addr = d.Address.String()
|
||||
}
|
||||
if d.Network == Network_TCP || d.Network == Network_UDP {
|
||||
addr = d.Address.String() + ":" + d.Port.String()
|
||||
} else if d.Network == Network_UNIX {
|
||||
addr = d.Address.String()
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
@@ -76,8 +76,9 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
ResolveUnixAddr = net.ResolveUnixAddr
|
||||
ResolveTCPAddr = net.ResolveTCPAddr
|
||||
ResolveUDPAddr = net.ResolveUDPAddr
|
||||
ResolveUnixAddr = net.ResolveUnixAddr
|
||||
)
|
||||
|
||||
type Resolver = net.Resolver
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/xtls/quic-go/quicvarint"
|
||||
"github.com/quic-go/quic-go/quicvarint"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/bytespool"
|
||||
|
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 {
|
||||
certPEM, keyPEM := certificate.ToPEM()
|
||||
return task.Run(context.Background(), func() error {
|
||||
return writeFile(certPEM, name+"_cert.pem")
|
||||
return writeFile(certPEM, name+".crt")
|
||||
}, func() error {
|
||||
return writeFile(keyPEM, name+"_key.pem")
|
||||
return writeFile(keyPEM, name+".key")
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
cnet "github.com/xtls/xray-core/common/net"
|
||||
@@ -32,6 +31,9 @@ func JSONMarshalWithoutEscape(t interface{}) ([]byte, error) {
|
||||
}
|
||||
|
||||
func marshalTypedMessage(v *cserial.TypedMessage, ignoreNullValue bool, insertTypeInfo bool) interface{} {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
tmsg, err := v.GetInstance()
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -56,7 +58,9 @@ func marshalSlice(v reflect.Value, ignoreNullValue bool, insertTypeInfo bool) in
|
||||
}
|
||||
|
||||
func isNullValue(f reflect.StructField, rv reflect.Value) bool {
|
||||
if rv.Kind() == reflect.String && rv.Len() == 0 {
|
||||
if rv.Kind() == reflect.Struct {
|
||||
return false
|
||||
} else if rv.Kind() == reflect.String && rv.Len() == 0 {
|
||||
return true
|
||||
} else if !isValueKind(rv.Kind()) && rv.IsNil() {
|
||||
return true
|
||||
@@ -182,6 +186,12 @@ func marshalKnownType(v interface{}, ignoreNullValue bool, insertTypeInfo bool)
|
||||
case *conf.PortList:
|
||||
cpl := v.(*conf.PortList)
|
||||
return serializePortList(cpl.Build())
|
||||
case conf.Int32Range:
|
||||
i32rng := v.(conf.Int32Range)
|
||||
if i32rng.Left == i32rng.Right {
|
||||
return i32rng.Left, true
|
||||
}
|
||||
return i32rng.String(), true
|
||||
case cnet.Address:
|
||||
if addr := v.(cnet.Address); addr != nil {
|
||||
return addr.String(), true
|
||||
@@ -192,28 +202,29 @@ func marshalKnownType(v interface{}, ignoreNullValue bool, insertTypeInfo bool)
|
||||
}
|
||||
}
|
||||
|
||||
var valueKinds = []reflect.Kind{
|
||||
reflect.Bool,
|
||||
reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Int64,
|
||||
reflect.Uint,
|
||||
reflect.Uint8,
|
||||
reflect.Uint16,
|
||||
reflect.Uint32,
|
||||
reflect.Uint64,
|
||||
reflect.Uintptr,
|
||||
reflect.Float32,
|
||||
reflect.Float64,
|
||||
reflect.Complex64,
|
||||
reflect.Complex128,
|
||||
reflect.String,
|
||||
}
|
||||
|
||||
func isValueKind(kind reflect.Kind) bool {
|
||||
return slices.Contains(valueKinds, kind)
|
||||
switch kind {
|
||||
case reflect.Bool,
|
||||
reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Int64,
|
||||
reflect.Uint,
|
||||
reflect.Uint8,
|
||||
reflect.Uint16,
|
||||
reflect.Uint32,
|
||||
reflect.Uint64,
|
||||
reflect.Uintptr,
|
||||
reflect.Float32,
|
||||
reflect.Float64,
|
||||
reflect.Complex64,
|
||||
reflect.Complex128,
|
||||
reflect.String:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func marshalInterface(v interface{}, ignoreNullValue bool, insertTypeInfo bool) interface{} {
|
||||
|
@@ -116,98 +116,129 @@ func TestMarshalConfigJson(t *testing.T) {
|
||||
"system",
|
||||
"inboundDownlink",
|
||||
"outboundUplink",
|
||||
"XHTTP_IN",
|
||||
"\"host\": \"bing.com\"",
|
||||
"scMaxEachPostBytes",
|
||||
"\"from\": 100",
|
||||
"\"to\": 1000",
|
||||
"\"from\": 1000000",
|
||||
"\"to\": 1000000",
|
||||
}
|
||||
for _, kw := range keywords {
|
||||
if !strings.Contains(tc, kw) {
|
||||
t.Error("marshaled config error")
|
||||
t.Log("config.json:", tc)
|
||||
t.Error("keyword not found:", kw)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getConfig() string {
|
||||
return `{
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
},
|
||||
"stats": {},
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserUplink": true,
|
||||
"statsUserDownlink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsInboundUplink": true,
|
||||
"statsInboundDownlink": true,
|
||||
"statsOutboundUplink": true,
|
||||
"statsOutboundDownlink": true
|
||||
}
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "agentin",
|
||||
"protocol": "http",
|
||||
"port": 8080,
|
||||
"listen": "127.0.0.1",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 10085,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
},
|
||||
"tag": "api-in"
|
||||
}
|
||||
],
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"StatsService"
|
||||
]
|
||||
},
|
||||
"routing": {
|
||||
"rules": [
|
||||
{
|
||||
"inboundTag": [
|
||||
"api-in"
|
||||
],
|
||||
"outboundTag": "api",
|
||||
"type": "field"
|
||||
}
|
||||
],
|
||||
"domainStrategy": "AsIs"
|
||||
},
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "1.2.3.4",
|
||||
"port": 1234,
|
||||
"users": [
|
||||
{
|
||||
"id": "4784f9b8-a879-4fec-9718-ebddefa47750",
|
||||
"encryption": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tag": "agentout",
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"security": "none",
|
||||
"wsSettings": {
|
||||
"path": "/?ed=2048",
|
||||
"host": "bing.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
},
|
||||
"stats": {},
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserUplink": true,
|
||||
"statsUserDownlink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsInboundUplink": true,
|
||||
"statsInboundDownlink": true,
|
||||
"statsOutboundUplink": true,
|
||||
"statsOutboundDownlink": true
|
||||
}
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "agentin",
|
||||
"protocol": "http",
|
||||
"port": 18080,
|
||||
"listen": "127.0.0.1",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 10085,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
},
|
||||
"tag": "api-in"
|
||||
}
|
||||
],
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"StatsService"
|
||||
]
|
||||
},
|
||||
"routing": {
|
||||
"rules": [
|
||||
{
|
||||
"inboundTag": [
|
||||
"api-in"
|
||||
],
|
||||
"outboundTag": "api",
|
||||
"type": "field"
|
||||
}
|
||||
],
|
||||
"domainStrategy": "AsIs"
|
||||
},
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "1.2.3.4",
|
||||
"port": 1234,
|
||||
"users": [
|
||||
{
|
||||
"id": "4784f9b8-a879-4fec-9718-ebddefa47750",
|
||||
"encryption": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tag": "XHTTP_IN",
|
||||
"streamSettings": {
|
||||
"network": "xhttp",
|
||||
"xhttpSettings": {
|
||||
"host": "bing.com",
|
||||
"path": "/xhttp_client_upload",
|
||||
"mode": "auto",
|
||||
"extra": {
|
||||
"noSSEHeader": false,
|
||||
"scMaxEachPostBytes": 1000000,
|
||||
"scMaxBufferedPosts": 30,
|
||||
"xPaddingBytes": "100-1000"
|
||||
}
|
||||
},
|
||||
"sockopt": {
|
||||
"tcpFastOpen": true,
|
||||
"acceptProxyProtocol": false,
|
||||
"tcpcongestion": "bbr",
|
||||
"tcpMptcp": true
|
||||
}
|
||||
},
|
||||
"sniffing": {
|
||||
"enabled": true,
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls",
|
||||
"quic"
|
||||
],
|
||||
"metadataOnly": false,
|
||||
"routeOnly": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
}
|
||||
|
@@ -23,6 +23,8 @@ const (
|
||||
timeoutOnlyKey ctx.SessionKey = 8
|
||||
allowedNetworkKey ctx.SessionKey = 9
|
||||
handlerSessionKey ctx.SessionKey = 10
|
||||
mitmAlpn11Key ctx.SessionKey = 11
|
||||
mitmServerNameKey ctx.SessionKey = 12
|
||||
)
|
||||
|
||||
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
|
||||
@@ -162,3 +164,25 @@ func AllowedNetworkFromContext(ctx context.Context) net.Network {
|
||||
}
|
||||
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 ""
|
||||
}
|
||||
|
@@ -18,8 +18,8 @@ import (
|
||||
|
||||
var (
|
||||
Version_x byte = 25
|
||||
Version_y byte = 1
|
||||
Version_z byte = 1
|
||||
Version_y byte = 2
|
||||
Version_z byte = 18
|
||||
)
|
||||
|
||||
var (
|
||||
|
@@ -359,7 +359,7 @@ func (s *Instance) AddFeature(feature features.Feature) error {
|
||||
}
|
||||
s.pendingOptionalResolutions = pendingOptional
|
||||
s.resolveLock.Unlock()
|
||||
|
||||
|
||||
var err error
|
||||
for _, r := range availableResolution {
|
||||
err = r.callbackResolution(s.features) // only return the last error for now
|
||||
|
@@ -25,7 +25,7 @@ type Handler interface {
|
||||
// xray:api:stable
|
||||
type Manager interface {
|
||||
features.Feature
|
||||
// GetHandlers returns an InboundHandler for the given tag.
|
||||
// GetHandler returns an InboundHandler for the given tag.
|
||||
GetHandler(ctx context.Context, tag string) (Handler, error)
|
||||
// AddHandler adds the given handler into this Manager.
|
||||
AddHandler(ctx context.Context, handler Handler) error
|
||||
|
@@ -11,7 +11,7 @@ type Context interface {
|
||||
// GetInboundTag returns the tag of the inbound the connection was from.
|
||||
GetInboundTag() string
|
||||
|
||||
// GetSourcesIPs returns the source IPs bound to the connection.
|
||||
// GetSourceIPs returns the source IPs bound to the connection.
|
||||
GetSourceIPs() []net.IP
|
||||
|
||||
// GetSourcePort returns the source port of the connection.
|
||||
|
@@ -2,6 +2,7 @@ package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -30,6 +31,8 @@ type OnlineMap interface {
|
||||
AddIP(string)
|
||||
// List is the current OnlineMap ip list.
|
||||
List() []string
|
||||
// IpTimeMap return client ips and their last access time.
|
||||
IpTimeMap() map[string]time.Time
|
||||
}
|
||||
|
||||
// Channel is the interface for stats channel.
|
||||
|
32
go.mod
32
go.mod
@@ -4,14 +4,15 @@ go 1.23
|
||||
|
||||
require (
|
||||
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/golang/mock v1.7.0-rc.1
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/miekg/dns v1.1.62
|
||||
github.com/miekg/dns v1.1.63
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pires/go-proxyproto v0.8.0
|
||||
github.com/quic-go/quic-go v0.49.0
|
||||
github.com/refraction-networking/utls v1.6.7
|
||||
github.com/sagernet/sing v0.5.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
@@ -19,17 +20,16 @@ require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||
github.com/vishvananda/netlink v1.3.0
|
||||
github.com/xtls/quic-go v0.48.2
|
||||
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/sync v0.11.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/grpc v1.69.2
|
||||
google.golang.org/protobuf v1.36.1
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489
|
||||
google.golang.org/grpc v1.70.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0
|
||||
h12.io/socks v1.0.3
|
||||
lukechampine.com/blake3 v1.3.0
|
||||
)
|
||||
@@ -48,14 +48,14 @@ require (
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
84
go.sum
84
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/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
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.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -38,8 +38,8 @@ github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0N
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
@@ -54,6 +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/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
|
||||
github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
|
||||
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/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
@@ -74,43 +76,41 @@ github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQ
|
||||
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xtls/quic-go v0.48.2 h1:59Gs+E9qtc9s0uniXYDA649gNEZlMWcNpFLyp9jfkuE=
|
||||
github.com/xtls/quic-go v0.48.2/go.mod h1:rcyY5J0JT+1d5pa5Y+FbCsXM7Zu79jE87ZSFOBfiH7Q=
|
||||
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/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
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/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
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-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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -119,21 +119,21 @@ 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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -141,12 +141,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/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
||||
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -156,8 +156,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 h1:P+U/06iIKPQ3DLcg+zBfSCia1luZ2msPZrJ8jYDFPs0=
|
||||
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/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
|
@@ -203,6 +203,24 @@ func (list *PortList) Build() *net.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
|
||||
func (list *PortList) UnmarshalJSON(data []byte) error {
|
||||
var listStr string
|
||||
|
@@ -12,13 +12,13 @@ import (
|
||||
)
|
||||
|
||||
type NameServerConfig struct {
|
||||
Address *Address
|
||||
ClientIP *Address
|
||||
Port uint16
|
||||
SkipFallback bool
|
||||
Domains []string
|
||||
ExpectIPs StringList
|
||||
QueryStrategy string
|
||||
Address *Address `json:"address"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Port uint16 `json:"port"`
|
||||
SkipFallback bool `json:"skipFallback"`
|
||||
Domains []string `json:"domains"`
|
||||
ExpectIPs StringList `json:"expectIps"`
|
||||
QueryStrategy string `json:"queryStrategy"`
|
||||
}
|
||||
|
||||
func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||
|
@@ -2,6 +2,7 @@ package conf
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
@@ -152,8 +153,9 @@ func (c *FreedomConfig) Build() (proto.Message, error) {
|
||||
func ParseNoise(noise *Noise) (*freedom.Noise, error) {
|
||||
var err error
|
||||
NConfig := new(freedom.Noise)
|
||||
noise.Packet = strings.TrimSpace(noise.Packet)
|
||||
|
||||
switch strings.ToLower(noise.Type) {
|
||||
switch noise.Type {
|
||||
case "rand":
|
||||
min, max, err := ParseRangeString(noise.Packet)
|
||||
if err != nil {
|
||||
@@ -166,18 +168,25 @@ func ParseNoise(noise *Noise) (*freedom.Noise, error) {
|
||||
}
|
||||
|
||||
case "str":
|
||||
//user input string
|
||||
NConfig.StrNoise = []byte(strings.TrimSpace(noise.Packet))
|
||||
// user input string
|
||||
NConfig.Packet = []byte(noise.Packet)
|
||||
|
||||
case "hex":
|
||||
// user input hex
|
||||
NConfig.Packet, err = hex.DecodeString(noise.Packet)
|
||||
if err != nil {
|
||||
return nil, errors.New("Invalid hex string").Base(err)
|
||||
}
|
||||
|
||||
case "base64":
|
||||
//user input base64
|
||||
NConfig.StrNoise, err = base64.StdEncoding.DecodeString(strings.TrimSpace(noise.Packet))
|
||||
// user input base64
|
||||
NConfig.Packet, err = base64.RawURLEncoding.DecodeString(strings.NewReplacer("+", "-", "/", "_", "=", "").Replace(noise.Packet))
|
||||
if err != nil {
|
||||
return nil, errors.New("Invalid base64 string")
|
||||
return nil, errors.New("Invalid base64 string").Base(err)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("Invalid packet, only rand/str/base64 are supported")
|
||||
return nil, errors.New("Invalid packet, only rand/str/hex/base64 are supported")
|
||||
}
|
||||
|
||||
if noise.Delay != nil {
|
||||
|
@@ -6,15 +6,21 @@ import (
|
||||
)
|
||||
|
||||
type MetricsConfig struct {
|
||||
Tag string `json:"tag"`
|
||||
Tag string `json:"tag"`
|
||||
Listen string `json:"listen"`
|
||||
}
|
||||
|
||||
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 == "" {
|
||||
return nil, errors.New("metrics tag can't be empty.")
|
||||
c.Tag = "Metrics"
|
||||
}
|
||||
|
||||
return &metrics.Config{
|
||||
Tag: c.Tag,
|
||||
Tag: c.Tag,
|
||||
Listen: c.Listen,
|
||||
}, nil
|
||||
}
|
||||
|
@@ -231,6 +231,7 @@ type SplitHTTPConfig struct {
|
||||
ScMaxEachPostBytes Int32Range `json:"scMaxEachPostBytes"`
|
||||
ScMinPostsIntervalMs Int32Range `json:"scMinPostsIntervalMs"`
|
||||
ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"`
|
||||
ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"`
|
||||
Xmux XmuxConfig `json:"xmux"`
|
||||
DownloadSettings *StreamConfig `json:"downloadSettings"`
|
||||
Extra json.RawMessage `json:"extra"`
|
||||
@@ -280,6 +281,10 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if c.XPaddingBytes != (Int32Range{}) && (c.XPaddingBytes.From <= 0 || c.XPaddingBytes.To <= 0) {
|
||||
return nil, errors.New("xPaddingBytes cannot be disabled")
|
||||
}
|
||||
|
||||
if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 {
|
||||
return nil, errors.New("maxConnections cannot be specified together with maxConcurrency")
|
||||
}
|
||||
@@ -303,6 +308,7 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
ScMaxEachPostBytes: newRangeConfig(c.ScMaxEachPostBytes),
|
||||
ScMinPostsIntervalMs: newRangeConfig(c.ScMinPostsIntervalMs),
|
||||
ScMaxBufferedPosts: c.ScMaxBufferedPosts,
|
||||
ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs),
|
||||
Xmux: &splithttp.XmuxConfig{
|
||||
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
|
||||
MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
|
||||
@@ -404,6 +410,8 @@ type TLSConfig struct {
|
||||
PinnedPeerCertificatePublicKeySha256 *[]string `json:"pinnedPeerCertificatePublicKeySha256"`
|
||||
CurvePreferences *StringList `json:"curvePreferences"`
|
||||
MasterKeyLog string `json:"masterKeyLog"`
|
||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -425,6 +433,13 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
if c.ALPN != nil && len(*c.ALPN) > 0 {
|
||||
config.NextProtocol = []string(*c.ALPN)
|
||||
}
|
||||
if len(config.NextProtocol) > 1 {
|
||||
for _, p := range config.NextProtocol {
|
||||
if tcp.IsFromMitm(p) {
|
||||
return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`)
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.CurvePreferences != nil && len(*c.CurvePreferences) > 0 {
|
||||
config.CurvePreferences = []string(*c.CurvePreferences)
|
||||
}
|
||||
@@ -435,7 +450,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
config.CipherSuites = c.CipherSuites
|
||||
config.Fingerprint = strings.ToLower(c.Fingerprint)
|
||||
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
|
||||
|
||||
@@ -463,6 +478,11 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
|
||||
config.MasterKeyLog = c.MasterKeyLog
|
||||
|
||||
if c.ServerNameToVerify != "" {
|
||||
return nil, errors.PrintRemovedFeatureError(`"serverNameToVerify"`, `"verifyPeerCertInNames"`)
|
||||
}
|
||||
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
@@ -27,5 +27,6 @@ var CmdAPI = &base.Command{
|
||||
cmdRemoveRules,
|
||||
cmdSourceIpBlock,
|
||||
cmdOnlineStats,
|
||||
cmdOnlineStatsIpList,
|
||||
},
|
||||
}
|
||||
|
@@ -13,25 +13,20 @@ import (
|
||||
var cmdBalancerInfo = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api bi [--server=127.0.0.1:8080] [balancer]...",
|
||||
Short: "balancer information",
|
||||
Short: "Retrieve balancer information",
|
||||
Long: `
|
||||
Get information of specified balancers, including health, strategy
|
||||
and selecting. If no balancer tag specified, get information of
|
||||
all balancers.
|
||||
Retrieve information of specified balancers, including health, strategy and selecting.
|
||||
If no balancer tag specified, information for all balancers is returned.
|
||||
|
||||
> Make sure you have "RoutingService" set in "config.api.services"
|
||||
of server config.
|
||||
> Ensure that "RoutingService" is enabled under "config.api.services" in the server configuration.
|
||||
|
||||
Arguments:
|
||||
|
||||
-json
|
||||
Use json output.
|
||||
|
||||
-s, -server <server:port>
|
||||
The API server address. Default 127.0.0.1:8080
|
||||
|
||||
-t, -timeout <seconds>
|
||||
Timeout seconds to call API. Default 3
|
||||
Timeout in seconds for calling API. Default 3
|
||||
|
||||
Example:
|
||||
|
||||
|
@@ -7,31 +7,27 @@ import (
|
||||
|
||||
var cmdBalancerOverride = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api bo [--server=127.0.0.1:8080] <-b balancer> outboundTag",
|
||||
Short: "balancer override",
|
||||
UsageLine: "{{.Exec}} api bo [--server=127.0.0.1:8080] <-b balancer> outboundTag <-r>",
|
||||
Short: "Override balancer",
|
||||
Long: `
|
||||
Override a balancer's selection.
|
||||
Override the selection target of a balancer.
|
||||
|
||||
> Make sure you have "RoutingService" set in "config.api.services"
|
||||
of server config.
|
||||
> Ensure that the "RoutingService" is properly configured under "config.api.services" in the server configuration.
|
||||
|
||||
Once a balancer's selecting is overridden:
|
||||
Once the balancer's selection is overridden:
|
||||
|
||||
- The balancer's selection result will always be outboundTag
|
||||
|
||||
Arguments:
|
||||
|
||||
-r, -remove
|
||||
Remove the overridden
|
||||
|
||||
-r, -remove
|
||||
Remove the override
|
||||
|
||||
-s, -server
|
||||
-s, -server <server:port>
|
||||
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
|
||||
|
||||
-r, -remove
|
||||
Remove the existing override.
|
||||
|
||||
Example:
|
||||
|
||||
|
@@ -8,20 +8,28 @@ import (
|
||||
var cmdInboundUser = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api inbounduser [--server=127.0.0.1:8080] -tag=tag [-email=email]",
|
||||
Short: "Get Inbound User",
|
||||
Short: "Retrieve inbound user(s)",
|
||||
Long: `
|
||||
Get User info from an inbound.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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
|
||||
Inbound tag
|
||||
-email
|
||||
User email. If email is not given, will get all users
|
||||
|
||||
-email
|
||||
The user's email address. If not provided, all users will be retrieved.
|
||||
|
||||
Example:
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name" -email="xray@love.com"
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name"
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="tag name" -email="xray@love.com"
|
||||
`,
|
||||
Run: executeInboundUser,
|
||||
}
|
||||
|
@@ -8,18 +8,24 @@ import (
|
||||
var cmdInboundUserCount = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api inboundusercount [--server=127.0.0.1:8080] -tag=tag",
|
||||
Short: "Get Inbound User Count",
|
||||
Short: "Retrieve inbound user count",
|
||||
Long: `
|
||||
Get User count from an inbound.
|
||||
Retrieve the user count for a specified inbound tag.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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
|
||||
Inbound tag
|
||||
Inbound tag
|
||||
|
||||
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,
|
||||
}
|
||||
|
@@ -15,13 +15,18 @@ var cmdAddInbounds = &base.Command{
|
||||
Short: "Add inbounds",
|
||||
Long: `
|
||||
Add inbounds to Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 c1.json c2.json
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
||||
`,
|
||||
Run: executeAddInbounds,
|
||||
}
|
||||
|
@@ -14,13 +14,18 @@ var cmdRemoveInbounds = &base.Command{
|
||||
Short: "Remove inbounds",
|
||||
Long: `
|
||||
Remove inbounds from Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 c1.json "tag name"
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
|
||||
`,
|
||||
Run: executeRemoveInbounds,
|
||||
}
|
||||
|
@@ -11,11 +11,18 @@ var cmdRestartLogger = &base.Command{
|
||||
Short: "Restart the logger",
|
||||
Long: `
|
||||
Restart the logger of Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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,
|
||||
}
|
||||
|
@@ -15,13 +15,18 @@ var cmdAddOutbounds = &base.Command{
|
||||
Short: "Add outbounds",
|
||||
Long: `
|
||||
Add outbounds to Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 c1.json c2.json
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
||||
`,
|
||||
Run: executeAddOutbounds,
|
||||
}
|
||||
|
@@ -14,13 +14,18 @@ var cmdRemoveOutbounds = &base.Command{
|
||||
Short: "Remove outbounds",
|
||||
Long: `
|
||||
Remove outbounds from Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 c1.json "tag name"
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
|
||||
`,
|
||||
Run: executeRemoveOutbounds,
|
||||
}
|
||||
|
@@ -16,16 +16,21 @@ var cmdAddRules = &base.Command{
|
||||
Short: "Add routing rules",
|
||||
Long: `
|
||||
Add routing rules to Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
The API server address. Default 127.0.0.1:8080
|
||||
-t, -timeout
|
||||
|
||||
-t, -timeout <seconds>
|
||||
Timeout seconds to call API. Default 3
|
||||
|
||||
-append
|
||||
append or replace config. Default false
|
||||
Append to the existing configuration instead of replacing it. Default false
|
||||
|
||||
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,
|
||||
}
|
||||
|
@@ -9,17 +9,22 @@ import (
|
||||
|
||||
var cmdRemoveRules = &base.Command{
|
||||
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",
|
||||
Long: `
|
||||
Remove routing rules by ruleTag from Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 ruleTag1 ruleTag2
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 ruleTag1 ruleTag2
|
||||
`,
|
||||
Run: executeRemoveRules,
|
||||
}
|
||||
|
@@ -14,25 +14,34 @@ import (
|
||||
var cmdSourceIpBlock = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api sib [--server=127.0.0.1:8080] -outbound=blocked -inbound=socks 1.2.3.4",
|
||||
Short: "Drop connections by source ip",
|
||||
Short: "Block connections by source IP",
|
||||
Long: `
|
||||
Drop connections by source ip.
|
||||
Block connections by source IP address.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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
|
||||
route traffic to specific outbound.
|
||||
Specifies the outbound tag.
|
||||
|
||||
-inbound
|
||||
target traffig from specific inbound.
|
||||
Specifies the inbound tag.
|
||||
|
||||
-ruletag
|
||||
set ruleTag. Default sourceIpBlock
|
||||
The ruleTag. Default sourceIpBlock
|
||||
|
||||
-reset
|
||||
remove ruletag and apply new source IPs. Default false
|
||||
|
||||
Example:
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
||||
Example:
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -outbound=blocked -inbound=socks 1.2.3.4
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -outbound=blocked -inbound=socks 1.2.3.4 -reset
|
||||
`,
|
||||
Run: executeSourceIpBlock,
|
||||
}
|
||||
|
@@ -8,19 +8,26 @@ import (
|
||||
var cmdGetStats = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api stats [--server=127.0.0.1:8080] [-name '']",
|
||||
Short: "Get statistics",
|
||||
Short: "Retrieve statistics",
|
||||
Long: `
|
||||
Get statistics from Xray.
|
||||
Retrieve the statistics from Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 of the stat counter.
|
||||
Name of the counter.
|
||||
|
||||
-reset
|
||||
Reset the counter to fetching its value.
|
||||
Reset the counter after fetching their values. Default false
|
||||
|
||||
Example:
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -name "inbound>>>statin>>>traffic>>>downlink"
|
||||
`,
|
||||
Run: executeGetStats,
|
||||
|
@@ -7,21 +7,25 @@ import (
|
||||
|
||||
var cmdOnlineStats = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api statsonline [--server=127.0.0.1:8080] [-name '']",
|
||||
Short: "Get online user",
|
||||
UsageLine: "{{.Exec}} api statsonline [--server=127.0.0.1:8080] [-email '']",
|
||||
Short: "Retrieve the online session count for a user",
|
||||
Long: `
|
||||
Get statistics from Xray.
|
||||
Retrieve the current number of active sessions for a user from Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 of the user.
|
||||
-reset
|
||||
Reset the counter to fetching its value.
|
||||
The user's email address.
|
||||
|
||||
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,
|
||||
}
|
||||
|
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",
|
||||
Long: `
|
||||
Query statistics from Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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 of the query.
|
||||
Filter pattern for the statistics query.
|
||||
|
||||
-reset
|
||||
Reset the counter to fetching its value.
|
||||
Reset the counter after fetching their values. Default false
|
||||
|
||||
Example:
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -pattern "counter_"
|
||||
`,
|
||||
Run: executeQueryStats,
|
||||
|
@@ -8,14 +8,21 @@ import (
|
||||
var cmdSysStats = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api statssys [--server=127.0.0.1:8080]",
|
||||
Short: "Get system statistics",
|
||||
Short: "Retrieve system statistics",
|
||||
Long: `
|
||||
Get system statistics from Xray.
|
||||
Retrieve system statistics from Xray.
|
||||
|
||||
Arguments:
|
||||
-s, -server
|
||||
|
||||
-s, -server <server:port>
|
||||
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,
|
||||
}
|
||||
|
@@ -42,7 +42,8 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
||||
// Modify random bytes using algorithm described at:
|
||||
// https://cr.yp.to/ecdh.html.
|
||||
privateKey[0] &= 248
|
||||
privateKey[31] &= 127 | 64
|
||||
privateKey[31] &= 127
|
||||
privateKey[31] |= 64
|
||||
|
||||
if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {
|
||||
output = err.Error()
|
||||
|
@@ -120,9 +120,9 @@ func writeFile(content []byte, name string) error {
|
||||
func printFile(certificate *cert.Certificate, name string) error {
|
||||
certPEM, keyPEM := certificate.ToPEM()
|
||||
return task.Run(context.Background(), func() error {
|
||||
return writeFile(certPEM, name+"_cert.pem")
|
||||
return writeFile(certPEM, name+".crt")
|
||||
}, 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() {
|
||||
cmdRun.Run = executeRun // break init loop
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -63,10 +64,6 @@ func (d *DokodemoDoor) policy() policy.Session {
|
||||
return p
|
||||
}
|
||||
|
||||
type hasHandshakeAddressContext interface {
|
||||
HandshakeAddressContext(ctx context.Context) net.Address
|
||||
}
|
||||
|
||||
// Process implements proxy.Inbound.
|
||||
func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||
errors.LogDebug(ctx, "processing connection from: ", conn.RemoteAddr())
|
||||
@@ -86,11 +83,14 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
||||
destinationOverridden = true
|
||||
}
|
||||
}
|
||||
if handshake, ok := conn.(hasHandshakeAddressContext); ok && !destinationOverridden {
|
||||
addr := handshake.HandshakeAddressContext(ctx)
|
||||
if addr != nil {
|
||||
dest.Address = addr
|
||||
if tlsConn, ok := conn.(tls.Interface); ok && !destinationOverridden {
|
||||
if serverName := tlsConn.HandshakeContextServerName(ctx); serverName != "" {
|
||||
dest.Address = net.DomainAddress(serverName)
|
||||
destinationOverridden = true
|
||||
ctx = session.ContextWithMitmServerName(ctx, serverName)
|
||||
}
|
||||
if tlsConn.NegotiatedProtocol() != "h2" {
|
||||
ctx = session.ContextWithMitmAlpn11(ctx, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -233,7 +233,7 @@ type Noise struct {
|
||||
LengthMax uint64 `protobuf:"varint,2,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"`
|
||||
DelayMin uint64 `protobuf:"varint,3,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||
DelayMax uint64 `protobuf:"varint,4,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||
StrNoise []byte `protobuf:"bytes,5,opt,name=str_noise,json=strNoise,proto3" json:"str_noise,omitempty"`
|
||||
Packet []byte `protobuf:"bytes,5,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Noise) Reset() {
|
||||
@@ -294,9 +294,9 @@ func (x *Noise) GetDelayMax() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Noise) GetStrNoise() []byte {
|
||||
func (x *Noise) GetPacket() []byte {
|
||||
if x != nil {
|
||||
return x.StrNoise
|
||||
return x.Packet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -412,7 +412,7 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{
|
||||
0x6c, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0x9c, 0x01, 0x0a, 0x05,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0x97, 0x01, 0x0a, 0x05,
|
||||
0x4e, 0x6f, 0x69, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f,
|
||||
0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74,
|
||||
0x68, 0x4d, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x6d,
|
||||
@@ -420,49 +420,49 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{
|
||||
0x4d, 0x61, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6e,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6e,
|
||||
0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x61, 0x78, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x73, 0x74, 0x72, 0x5f, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x08, 0x73, 0x74, 0x72, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x22, 0x97, 0x04, 0x0a, 0x06, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f,
|
||||
0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29,
|
||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65,
|
||||
0x64, 0x6f, 0x6d, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x5a, 0x0a, 0x14, 0x64, 0x65, 0x73,
|
||||
0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
|
||||
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65, 0x73,
|
||||
0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x61, 0x78, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70,
|
||||
0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x97, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74,
|
||||
0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x12, 0x5a, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
||||
0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x13, 0x64, 0x65, 0x73,
|
||||
0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
|
||||
0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65,
|
||||
0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65,
|
||||
0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c,
|
||||
0x65, 0x76, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x46, 0x72, 0x61, 0x67,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25,
|
||||
0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x31, 0x0a, 0x06, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x18,
|
||||
0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x4e, 0x6f, 0x69, 0x73, 0x65,
|
||||
0x52, 0x06, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41,
|
||||
0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50,
|
||||
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12,
|
||||
0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08,
|
||||
0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53,
|
||||
0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43,
|
||||
0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f,
|
||||
0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49,
|
||||
0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50,
|
||||
0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50,
|
||||
0x36, 0x34, 0x10, 0x0a, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01,
|
||||
0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2f, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12,
|
||||
0x38, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66,
|
||||
0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52,
|
||||
0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f,
|
||||
0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||
0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||
0x12, 0x31, 0x0a, 0x06, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72,
|
||||
0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x52, 0x06, 0x6e, 0x6f, 0x69,
|
||||
0x73, 0x65, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
|
||||
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10,
|
||||
0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a,
|
||||
0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53,
|
||||
0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49,
|
||||
0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36,
|
||||
0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10,
|
||||
0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07,
|
||||
0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12,
|
||||
0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12,
|
||||
0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42,
|
||||
0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65,
|
||||
0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -25,7 +25,7 @@ message Noise {
|
||||
uint64 length_max = 2;
|
||||
uint64 delay_min = 3;
|
||||
uint64 delay_max = 4;
|
||||
bytes str_noise = 5;
|
||||
bytes packet = 5;
|
||||
}
|
||||
|
||||
message Config {
|
||||
|
@@ -266,6 +266,9 @@ func isTLSConn(conn stat.Connection) bool {
|
||||
if _, ok := conn.(*tls.Conn); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := conn.(*tls.UConn); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -407,8 +410,8 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
var err error
|
||||
for _, n := range w.noises {
|
||||
//User input string or base64 encoded string
|
||||
if n.StrNoise != nil {
|
||||
noise = n.StrNoise
|
||||
if n.Packet != nil {
|
||||
noise = n.Packet
|
||||
} else {
|
||||
//Random noise
|
||||
noise, err = GenerateRandomBytes(randBetween(int64(n.LengthMin),
|
||||
|
@@ -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))
|
||||
}
|
||||
responseFunc := func() error {
|
||||
ob.CanSpliceCopy = 1
|
||||
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||
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 {
|
||||
inbound.CanSpliceCopy = 1
|
||||
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
||||
|
||||
v2writer := buf.NewWriter(conn)
|
||||
|
198
proxy/proxy.go
198
proxy/proxy.go
@@ -107,37 +107,65 @@ type TrafficState struct {
|
||||
IsTLS bool
|
||||
Cipher uint16
|
||||
RemainingServerHello int32
|
||||
Inbound InboundState
|
||||
Outbound OutboundState
|
||||
}
|
||||
|
||||
type InboundState struct {
|
||||
// reader link state
|
||||
WithinPaddingBuffers bool
|
||||
UplinkReaderDirectCopy bool
|
||||
RemainingCommand int32
|
||||
RemainingContent int32
|
||||
RemainingPadding int32
|
||||
CurrentCommand int
|
||||
// write link state
|
||||
IsPadding bool
|
||||
DownlinkWriterDirectCopy bool
|
||||
}
|
||||
|
||||
type OutboundState struct {
|
||||
// reader link state
|
||||
WithinPaddingBuffers bool
|
||||
ReaderSwitchToDirectCopy bool
|
||||
DownlinkReaderDirectCopy bool
|
||||
RemainingCommand int32
|
||||
RemainingContent int32
|
||||
RemainingPadding int32
|
||||
CurrentCommand int
|
||||
|
||||
// write link state
|
||||
IsPadding bool
|
||||
WriterSwitchToDirectCopy bool
|
||||
IsPadding bool
|
||||
UplinkWriterDirectCopy bool
|
||||
}
|
||||
|
||||
func NewTrafficState(userUUID []byte) *TrafficState {
|
||||
return &TrafficState{
|
||||
UserUUID: userUUID,
|
||||
NumberOfPacketToFilter: 8,
|
||||
EnableXtls: false,
|
||||
IsTLS12orAbove: false,
|
||||
IsTLS: false,
|
||||
Cipher: 0,
|
||||
RemainingServerHello: -1,
|
||||
WithinPaddingBuffers: true,
|
||||
ReaderSwitchToDirectCopy: false,
|
||||
RemainingCommand: -1,
|
||||
RemainingContent: -1,
|
||||
RemainingPadding: -1,
|
||||
CurrentCommand: 0,
|
||||
IsPadding: true,
|
||||
WriterSwitchToDirectCopy: false,
|
||||
UserUUID: userUUID,
|
||||
NumberOfPacketToFilter: 8,
|
||||
EnableXtls: false,
|
||||
IsTLS12orAbove: false,
|
||||
IsTLS: false,
|
||||
Cipher: 0,
|
||||
RemainingServerHello: -1,
|
||||
Inbound: InboundState{
|
||||
WithinPaddingBuffers: true,
|
||||
UplinkReaderDirectCopy: false,
|
||||
RemainingCommand: -1,
|
||||
RemainingContent: -1,
|
||||
RemainingPadding: -1,
|
||||
CurrentCommand: 0,
|
||||
IsPadding: true,
|
||||
DownlinkWriterDirectCopy: false,
|
||||
},
|
||||
Outbound: OutboundState{
|
||||
WithinPaddingBuffers: true,
|
||||
DownlinkReaderDirectCopy: false,
|
||||
RemainingCommand: -1,
|
||||
RemainingContent: -1,
|
||||
RemainingPadding: -1,
|
||||
CurrentCommand: 0,
|
||||
IsPadding: true,
|
||||
UplinkWriterDirectCopy: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,37 +175,58 @@ type VisionReader struct {
|
||||
buf.Reader
|
||||
trafficState *TrafficState
|
||||
ctx context.Context
|
||||
isUplink bool
|
||||
}
|
||||
|
||||
func NewVisionReader(reader buf.Reader, state *TrafficState, context context.Context) *VisionReader {
|
||||
func NewVisionReader(reader buf.Reader, state *TrafficState, isUplink bool, context context.Context) *VisionReader {
|
||||
return &VisionReader{
|
||||
Reader: reader,
|
||||
trafficState: state,
|
||||
ctx: context,
|
||||
isUplink: isUplink,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
buffer, err := w.Reader.ReadMultiBuffer()
|
||||
if !buffer.IsEmpty() {
|
||||
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))
|
||||
for _, b := range buffer {
|
||||
newbuffer := XtlsUnpadding(b, w.trafficState, w.ctx)
|
||||
newbuffer := XtlsUnpadding(b, w.trafficState, w.isUplink, w.ctx)
|
||||
if newbuffer.Len() > 0 {
|
||||
mb2 = append(mb2, newbuffer)
|
||||
}
|
||||
}
|
||||
buffer = mb2
|
||||
if w.trafficState.RemainingContent > 0 || w.trafficState.RemainingPadding > 0 || w.trafficState.CurrentCommand == 0 {
|
||||
w.trafficState.WithinPaddingBuffers = true
|
||||
} else if w.trafficState.CurrentCommand == 1 {
|
||||
w.trafficState.WithinPaddingBuffers = false
|
||||
} else if w.trafficState.CurrentCommand == 2 {
|
||||
w.trafficState.WithinPaddingBuffers = false
|
||||
w.trafficState.ReaderSwitchToDirectCopy = true
|
||||
if *remainingContent > 0 || *remainingPadding > 0 || *currentCommand == 0 {
|
||||
*withinPaddingBuffers = true
|
||||
} else if *currentCommand == 1 {
|
||||
*withinPaddingBuffers = false
|
||||
} else if *currentCommand == 2 {
|
||||
*withinPaddingBuffers = false
|
||||
*switchToDirectCopy = true
|
||||
} else {
|
||||
errors.LogInfo(w.ctx, "XtlsRead unknown command ", w.trafficState.CurrentCommand, buffer.Len())
|
||||
errors.LogInfo(w.ctx, "XtlsRead unknown command ", *currentCommand, buffer.Len())
|
||||
}
|
||||
}
|
||||
if w.trafficState.NumberOfPacketToFilter > 0 {
|
||||
@@ -194,9 +243,10 @@ type VisionWriter struct {
|
||||
trafficState *TrafficState
|
||||
ctx context.Context
|
||||
writeOnceUserUUID []byte
|
||||
isUplink bool
|
||||
}
|
||||
|
||||
func NewVisionWriter(writer buf.Writer, state *TrafficState, context context.Context) *VisionWriter {
|
||||
func NewVisionWriter(writer buf.Writer, state *TrafficState, isUplink bool, context context.Context) *VisionWriter {
|
||||
w := make([]byte, len(state.UserUUID))
|
||||
copy(w, state.UserUUID)
|
||||
return &VisionWriter{
|
||||
@@ -204,6 +254,7 @@ func NewVisionWriter(writer buf.Writer, state *TrafficState, context context.Con
|
||||
trafficState: state,
|
||||
ctx: context,
|
||||
writeOnceUserUUID: w,
|
||||
isUplink: isUplink,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +262,16 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
if w.trafficState.NumberOfPacketToFilter > 0 {
|
||||
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 {
|
||||
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
|
||||
return w.Writer.WriteMultiBuffer(mb)
|
||||
@@ -221,7 +281,7 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
for i, b := range mb {
|
||||
if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) {
|
||||
if w.trafficState.EnableXtls {
|
||||
w.trafficState.WriterSwitchToDirectCopy = true
|
||||
*switchToDirectCopy = true
|
||||
}
|
||||
var command byte = CommandPaddingContinue
|
||||
if i == len(mb)-1 {
|
||||
@@ -231,16 +291,16 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
}
|
||||
}
|
||||
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
|
||||
continue
|
||||
} 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)
|
||||
break
|
||||
}
|
||||
var command byte = CommandPaddingContinue
|
||||
if i == len(mb)-1 && !w.trafficState.IsPadding {
|
||||
if i == len(mb)-1 && !*isPadding {
|
||||
command = CommandPaddingEnd
|
||||
if w.trafficState.EnableXtls {
|
||||
command = CommandPaddingDirect
|
||||
@@ -327,38 +387,53 @@ func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool
|
||||
}
|
||||
|
||||
// XtlsUnpadding remove padding and parse command
|
||||
func XtlsUnpadding(b *buf.Buffer, s *TrafficState, ctx context.Context) *buf.Buffer {
|
||||
if s.RemainingCommand == -1 && s.RemainingContent == -1 && s.RemainingPadding == -1 { // initial state
|
||||
func XtlsUnpadding(b *buf.Buffer, s *TrafficState, isUplink bool, ctx context.Context) *buf.Buffer {
|
||||
var remainingCommand *int32
|
||||
var remainingContent *int32
|
||||
var remainingPadding *int32
|
||||
var currentCommand *int
|
||||
if isUplink {
|
||||
remainingCommand = &s.Inbound.RemainingCommand
|
||||
remainingContent = &s.Inbound.RemainingContent
|
||||
remainingPadding = &s.Inbound.RemainingPadding
|
||||
currentCommand = &s.Inbound.CurrentCommand
|
||||
} else {
|
||||
remainingCommand = &s.Outbound.RemainingCommand
|
||||
remainingContent = &s.Outbound.RemainingContent
|
||||
remainingPadding = &s.Outbound.RemainingPadding
|
||||
currentCommand = &s.Outbound.CurrentCommand
|
||||
}
|
||||
if *remainingCommand == -1 && *remainingContent == -1 && *remainingPadding == -1 { // initial state
|
||||
if b.Len() >= 21 && bytes.Equal(s.UserUUID, b.BytesTo(16)) {
|
||||
b.Advance(16)
|
||||
s.RemainingCommand = 5
|
||||
*remainingCommand = 5
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
newbuffer := buf.New()
|
||||
for b.Len() > 0 {
|
||||
if s.RemainingCommand > 0 {
|
||||
if *remainingCommand > 0 {
|
||||
data, err := b.ReadByte()
|
||||
if err != nil {
|
||||
return newbuffer
|
||||
}
|
||||
switch s.RemainingCommand {
|
||||
switch *remainingCommand {
|
||||
case 5:
|
||||
s.CurrentCommand = int(data)
|
||||
*currentCommand = int(data)
|
||||
case 4:
|
||||
s.RemainingContent = int32(data) << 8
|
||||
*remainingContent = int32(data) << 8
|
||||
case 3:
|
||||
s.RemainingContent = s.RemainingContent | int32(data)
|
||||
*remainingContent = *remainingContent | int32(data)
|
||||
case 2:
|
||||
s.RemainingPadding = int32(data) << 8
|
||||
*remainingPadding = int32(data) << 8
|
||||
case 1:
|
||||
s.RemainingPadding = s.RemainingPadding | int32(data)
|
||||
errors.LogInfo(ctx, "Xtls Unpadding new block, content ", s.RemainingContent, " padding ", s.RemainingPadding, " command ", s.CurrentCommand)
|
||||
*remainingPadding = *remainingPadding | int32(data)
|
||||
errors.LogInfo(ctx, "Xtls Unpadding new block, content ", *remainingContent, " padding ", *remainingPadding, " command ", *currentCommand)
|
||||
}
|
||||
s.RemainingCommand--
|
||||
} else if s.RemainingContent > 0 {
|
||||
len := s.RemainingContent
|
||||
*remainingCommand--
|
||||
} else if *remainingContent > 0 {
|
||||
len := *remainingContent
|
||||
if b.Len() < len {
|
||||
len = b.Len()
|
||||
}
|
||||
@@ -367,22 +442,22 @@ func XtlsUnpadding(b *buf.Buffer, s *TrafficState, ctx context.Context) *buf.Buf
|
||||
return newbuffer
|
||||
}
|
||||
newbuffer.Write(data)
|
||||
s.RemainingContent -= len
|
||||
*remainingContent -= len
|
||||
} else { // remainingPadding > 0
|
||||
len := s.RemainingPadding
|
||||
len := *remainingPadding
|
||||
if b.Len() < len {
|
||||
len = b.Len()
|
||||
}
|
||||
b.Advance(len)
|
||||
s.RemainingPadding -= len
|
||||
*remainingPadding -= len
|
||||
}
|
||||
if s.RemainingCommand <= 0 && s.RemainingContent <= 0 && s.RemainingPadding <= 0 { // this block done
|
||||
if s.CurrentCommand == 0 {
|
||||
s.RemainingCommand = 5
|
||||
if *remainingCommand <= 0 && *remainingContent <= 0 && *remainingPadding <= 0 { // this block done
|
||||
if *currentCommand == 0 {
|
||||
*remainingCommand = 5
|
||||
} else {
|
||||
s.RemainingCommand = -1 // set to initial state
|
||||
s.RemainingContent = -1
|
||||
s.RemainingPadding = -1
|
||||
*remainingCommand = -1 // set to initial state
|
||||
*remainingContent = -1
|
||||
*remainingPadding = -1
|
||||
if b.Len() > 0 { // shouldn't happen
|
||||
newbuffer.Write(b.Bytes())
|
||||
}
|
||||
@@ -449,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) {
|
||||
var readCounter, writerCounter stats.Counter
|
||||
if conn != nil {
|
||||
@@ -472,6 +547,9 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
||||
conn = pc.Raw()
|
||||
// 8192 > 4096, there is no need to process pc's bufReader
|
||||
}
|
||||
if uc, ok := conn.(*internet.UDSWrapperConn); ok {
|
||||
conn = uc.Conn
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
responseFunc = func() error {
|
||||
ob.CanSpliceCopy = 1
|
||||
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||
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))
|
||||
}
|
||||
responseFunc = func() error {
|
||||
ob.CanSpliceCopy = 1
|
||||
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||
reader := &UDPReader{Reader: udpConn}
|
||||
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
||||
|
@@ -2,6 +2,7 @@ package socks
|
||||
|
||||
import (
|
||||
"context"
|
||||
goerrors "errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
@@ -78,7 +79,13 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
|
||||
switch network {
|
||||
case net.Network_TCP:
|
||||
firstbyte := make([]byte, 1)
|
||||
conn.Read(firstbyte)
|
||||
if n, err := conn.Read(firstbyte); n == 0 {
|
||||
if goerrors.Is(err, io.EOF) {
|
||||
errors.LogInfo(ctx, "Connection closed immediately, likely health check connection")
|
||||
return nil
|
||||
}
|
||||
return errors.New("failed to read from connection").Base(err)
|
||||
}
|
||||
if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if it is Socks5/4/4a
|
||||
errors.LogDebug(ctx, "Not Socks request, try to parse as HTTP request")
|
||||
return s.httpServer.ProcessWithFirstbyte(ctx, network, conn, dispatcher, firstbyte...)
|
||||
@@ -192,6 +199,7 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
|
||||
}
|
||||
|
||||
responseDone := func() error {
|
||||
inbound.CanSpliceCopy = 1
|
||||
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
||||
|
||||
v2writer := buf.NewWriter(writer)
|
||||
@@ -249,6 +257,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
|
||||
if inbound != nil && inbound.Source.IsValid() {
|
||||
errors.LogInfo(ctx, "client UDP connection from ", inbound.Source)
|
||||
}
|
||||
inbound.CanSpliceCopy = 1
|
||||
|
||||
var dest *net.Destination
|
||||
|
||||
|
@@ -61,13 +61,13 @@ func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) {
|
||||
}
|
||||
|
||||
// EncodeBodyAddons returns a Writer that auto-encrypt content written by caller.
|
||||
func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *proxy.TrafficState, context context.Context) buf.Writer {
|
||||
func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *proxy.TrafficState, isUplink bool, context context.Context) buf.Writer {
|
||||
if request.Command == protocol.RequestCommandUDP {
|
||||
return NewMultiLengthPacketWriter(writer.(buf.Writer))
|
||||
}
|
||||
w := buf.NewWriter(writer)
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
w = proxy.NewVisionWriter(w, state, context)
|
||||
w = proxy.NewVisionWriter(w, state, isUplink, context)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
@@ -172,19 +172,19 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
|
||||
}
|
||||
|
||||
// XtlsRead filter and read xtls protocol
|
||||
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, 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 {
|
||||
for {
|
||||
if trafficState.ReaderSwitchToDirectCopy {
|
||||
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||
var writerConn net.Conn
|
||||
var inTimer *signal.ActivityTimer
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
|
||||
writerConn = inbound.Conn
|
||||
inTimer = inbound.Timer
|
||||
if inbound.CanSpliceCopy == 2 {
|
||||
if isUplink && inbound.CanSpliceCopy == 2 {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer,
|
||||
buffer, err := reader.ReadMultiBuffer()
|
||||
if !buffer.IsEmpty() {
|
||||
timer.Update()
|
||||
if trafficState.ReaderSwitchToDirectCopy {
|
||||
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||
// XTLS Vision processes struct TLS Conn's input and rawInput
|
||||
if inputBuffer, err := buf.ReadFrom(input); err == nil {
|
||||
if !inputBuffer.IsEmpty() {
|
||||
@@ -222,24 +222,28 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer,
|
||||
}
|
||||
|
||||
// XtlsWrite filter and write xtls protocol
|
||||
func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, trafficState *proxy.TrafficState, ob *session.Outbound, ctx context.Context) error {
|
||||
func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
||||
err := func() error {
|
||||
var ct stats.Counter
|
||||
for {
|
||||
buffer, err := reader.ReadMultiBuffer()
|
||||
if trafficState.WriterSwitchToDirectCopy {
|
||||
if isUplink && trafficState.Outbound.UplinkWriterDirectCopy || !isUplink && trafficState.Inbound.DownlinkWriterDirectCopy {
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
if inbound.CanSpliceCopy == 2 {
|
||||
if !isUplink && inbound.CanSpliceCopy == 2 {
|
||||
inbound.CanSpliceCopy = 1
|
||||
}
|
||||
if ob != nil && ob.CanSpliceCopy == 2 {
|
||||
if isUplink && ob != nil && ob.CanSpliceCopy == 2 {
|
||||
ob.CanSpliceCopy = 1
|
||||
}
|
||||
}
|
||||
rawConn, _, writerCounter := proxy.UnwrapRawConn(conn)
|
||||
writer = buf.NewWriter(rawConn)
|
||||
ct = writerCounter
|
||||
trafficState.WriterSwitchToDirectCopy = false
|
||||
if isUplink {
|
||||
trafficState.Outbound.UplinkWriterDirectCopy = false
|
||||
} else {
|
||||
trafficState.Inbound.DownlinkWriterDirectCopy = false
|
||||
}
|
||||
}
|
||||
if !buffer.IsEmpty() {
|
||||
if ct != nil {
|
||||
|
@@ -538,8 +538,8 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
||||
clientReader = proxy.NewVisionReader(clientReader, trafficState, ctx1)
|
||||
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, ctx1)
|
||||
clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1)
|
||||
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, true, ctx1)
|
||||
} else {
|
||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
|
||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||
@@ -561,7 +561,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
}
|
||||
|
||||
// default: clientWriter := bufferWriter
|
||||
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, ctx)
|
||||
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, false, ctx)
|
||||
multiBuffer, err1 := serverReader.ReadMultiBuffer()
|
||||
if err1 != nil {
|
||||
return err1 // ...
|
||||
@@ -576,7 +576,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
|
||||
var err error
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, ctx)
|
||||
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, false, ctx)
|
||||
} else {
|
||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
|
||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||
|
@@ -194,7 +194,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
}
|
||||
|
||||
// default: serverWriter := bufferWriter
|
||||
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, ctx)
|
||||
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, true, ctx)
|
||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||
serverWriter = xudp.NewPacketWriter(serverWriter, target, xudp.GetGlobalID(ctx))
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
}
|
||||
}
|
||||
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
||||
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, ctx1)
|
||||
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, true, ctx1)
|
||||
} else {
|
||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
|
||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||
@@ -261,7 +261,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
// default: serverReader := buf.NewReader(conn)
|
||||
serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
serverReader = proxy.NewVisionReader(serverReader, trafficState, ctx)
|
||||
serverReader = proxy.NewVisionReader(serverReader, trafficState, false, ctx)
|
||||
}
|
||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
@@ -272,7 +272,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
}
|
||||
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, ctx)
|
||||
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, false, ctx)
|
||||
} else {
|
||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
|
||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||
|
@@ -157,7 +157,7 @@ func (tun *netTun) Write(buf [][]byte, offset int) (int, error) {
|
||||
// WriteNotify implements channel.Notification
|
||||
func (tun *netTun) WriteNotify() {
|
||||
pkt := tun.ep.Read()
|
||||
if pkt.IsNil() {
|
||||
if pkt == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -194,7 +194,7 @@ func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
|
||||
Timeout: 15 * time.Second,
|
||||
})
|
||||
|
||||
handler(xnet.UDPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewUDPConn(stack, &wq, ep))
|
||||
handler(xnet.UDPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewUDPConn(&wq, ep))
|
||||
}(r)
|
||||
})
|
||||
stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -17,6 +18,12 @@ import (
|
||||
//go:embed dialer.html
|
||||
var webpage []byte
|
||||
|
||||
type task struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Extra any `json:"extra,omitempty"`
|
||||
}
|
||||
|
||||
var conns chan *websocket.Conn
|
||||
|
||||
var upgrader = &websocket.Upgrader{
|
||||
@@ -55,23 +62,69 @@ func HasBrowserDialer() bool {
|
||||
return conns != nil
|
||||
}
|
||||
|
||||
type webSocketExtra struct {
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
}
|
||||
|
||||
func DialWS(uri string, ed []byte) (*websocket.Conn, error) {
|
||||
data := []byte("WS " + uri)
|
||||
if ed != nil {
|
||||
data = append(data, " "+base64.RawURLEncoding.EncodeToString(ed)...)
|
||||
task := task{
|
||||
Method: "WS",
|
||||
URL: uri,
|
||||
}
|
||||
|
||||
return dialRaw(data)
|
||||
if ed != nil {
|
||||
task.Extra = webSocketExtra{
|
||||
Protocol: base64.RawURLEncoding.EncodeToString(ed),
|
||||
}
|
||||
}
|
||||
|
||||
return dialTask(task)
|
||||
}
|
||||
|
||||
func DialGet(uri string) (*websocket.Conn, error) {
|
||||
data := []byte("GET " + uri)
|
||||
return dialRaw(data)
|
||||
type httpExtra struct {
|
||||
Referrer string `json:"referrer,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
func DialPost(uri string, payload []byte) error {
|
||||
data := []byte("POST " + uri)
|
||||
conn, err := dialRaw(data)
|
||||
func httpExtraFromHeaders(headers http.Header) *httpExtra {
|
||||
if len(headers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
extra := httpExtra{}
|
||||
if referrer := headers.Get("Referer"); referrer != "" {
|
||||
extra.Referrer = referrer
|
||||
headers.Del("Referer")
|
||||
}
|
||||
|
||||
if len(headers) > 0 {
|
||||
extra.Headers = make(map[string]string)
|
||||
for header := range headers {
|
||||
extra.Headers[header] = headers.Get(header)
|
||||
}
|
||||
}
|
||||
|
||||
return &extra
|
||||
}
|
||||
|
||||
func DialGet(uri string, headers http.Header) (*websocket.Conn, error) {
|
||||
task := task{
|
||||
Method: "GET",
|
||||
URL: uri,
|
||||
Extra: httpExtraFromHeaders(headers),
|
||||
}
|
||||
|
||||
return dialTask(task)
|
||||
}
|
||||
|
||||
func DialPost(uri string, headers http.Header, payload []byte) error {
|
||||
task := task{
|
||||
Method: "POST",
|
||||
URL: uri,
|
||||
Extra: httpExtraFromHeaders(headers),
|
||||
}
|
||||
|
||||
conn, err := dialTask(task)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -90,7 +143,12 @@ func DialPost(uri string, payload []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func dialRaw(data []byte) (*websocket.Conn, error) {
|
||||
func dialTask(task task) (*websocket.Conn, error) {
|
||||
data, err := json.Marshal(task)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var conn *websocket.Conn
|
||||
for {
|
||||
conn = <-conns
|
||||
@@ -100,7 +158,7 @@ func dialRaw(data []byte) (*websocket.Conn, error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
err := CheckOK(conn)
|
||||
err = CheckOK(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -14,10 +14,28 @@
|
||||
let upstreamGetCount = 0;
|
||||
let upstreamWsCount = 0;
|
||||
let upstreamPostCount = 0;
|
||||
|
||||
function prepareRequestInit(extra) {
|
||||
const requestInit = {};
|
||||
if (extra.referrer) {
|
||||
// note: we have to strip the protocol and host part.
|
||||
// Browsers disallow that, and will reset the value to current page if attempted.
|
||||
const referrer = URL.parse(extra.referrer);
|
||||
requestInit.referrer = referrer.pathname + referrer.search + referrer.hash;
|
||||
requestInit.referrerPolicy = "unsafe-url";
|
||||
}
|
||||
|
||||
if (extra.headers) {
|
||||
requestInit.headers = extra.headers;
|
||||
}
|
||||
|
||||
return requestInit;
|
||||
}
|
||||
|
||||
let check = function () {
|
||||
if (clientIdleCount > 0) {
|
||||
return;
|
||||
};
|
||||
}
|
||||
clientIdleCount += 1;
|
||||
console.log("Prepare", url);
|
||||
let ws = new WebSocket(url);
|
||||
@@ -29,12 +47,12 @@
|
||||
// double-checking that this continues to work
|
||||
ws.onmessage = function (event) {
|
||||
clientIdleCount -= 1;
|
||||
let [method, url, protocol] = event.data.split(" ");
|
||||
switch (method) {
|
||||
let task = JSON.parse(event.data);
|
||||
switch (task.method) {
|
||||
case "WS": {
|
||||
upstreamWsCount += 1;
|
||||
console.log("Dial WS", url, protocol);
|
||||
const wss = new WebSocket(url, protocol);
|
||||
console.log("Dial WS", task.url, task.extra.protocol);
|
||||
const wss = new WebSocket(task.url, task.extra.protocol);
|
||||
wss.binaryType = "arraybuffer";
|
||||
let opened = false;
|
||||
ws.onmessage = function (event) {
|
||||
@@ -60,10 +78,12 @@
|
||||
wss.close()
|
||||
};
|
||||
break;
|
||||
};
|
||||
}
|
||||
case "GET": {
|
||||
(async () => {
|
||||
console.log("Dial GET", url);
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
|
||||
console.log("Dial GET", task.url);
|
||||
ws.send("ok");
|
||||
const controller = new AbortController();
|
||||
|
||||
@@ -83,58 +103,62 @@
|
||||
ws.onclose = (event) => {
|
||||
try {
|
||||
reader && reader.cancel();
|
||||
} catch(e) {};
|
||||
} catch(e) {}
|
||||
|
||||
try {
|
||||
controller.abort();
|
||||
} catch(e) {};
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
try {
|
||||
upstreamGetCount += 1;
|
||||
const response = await fetch(url, {signal: controller.signal});
|
||||
|
||||
requestInit.signal = controller.signal;
|
||||
const response = await fetch(task.url, requestInit);
|
||||
|
||||
const body = await response.body;
|
||||
reader = body.getReader();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
ws.send(value);
|
||||
if (value) ws.send(value); // don't send back "undefined" string when received nothing
|
||||
if (done) break;
|
||||
};
|
||||
}
|
||||
} finally {
|
||||
upstreamGetCount -= 1;
|
||||
console.log("Dial GET DONE, remaining: ", upstreamGetCount);
|
||||
ws.close();
|
||||
};
|
||||
}
|
||||
})();
|
||||
break;
|
||||
};
|
||||
}
|
||||
case "POST": {
|
||||
upstreamPostCount += 1;
|
||||
console.log("Dial POST", url);
|
||||
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
requestInit.method = "POST";
|
||||
|
||||
console.log("Dial POST", task.url);
|
||||
ws.send("ok");
|
||||
ws.onmessage = async (event) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
url,
|
||||
{method: "POST", body: event.data}
|
||||
);
|
||||
requestInit.body = event.data;
|
||||
const response = await fetch(task.url, requestInit);
|
||||
if (response.ok) {
|
||||
ws.send("ok");
|
||||
} else {
|
||||
console.error("bad status code");
|
||||
ws.send("fail");
|
||||
};
|
||||
}
|
||||
} finally {
|
||||
upstreamPostCount -= 1;
|
||||
console.log("Dial POST DONE, remaining: ", upstreamPostCount);
|
||||
ws.close();
|
||||
};
|
||||
}
|
||||
};
|
||||
break;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
};
|
||||
|
@@ -180,12 +180,12 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
||||
prefix := []byte("https://" + uConn.ServerName)
|
||||
maps.Lock()
|
||||
if maps.maps == nil {
|
||||
maps.maps = make(map[string]map[string]bool)
|
||||
maps.maps = make(map[string]map[string]struct{})
|
||||
}
|
||||
paths := maps.maps[uConn.ServerName]
|
||||
if paths == nil {
|
||||
paths = make(map[string]bool)
|
||||
paths[config.SpiderX] = true
|
||||
paths = make(map[string]struct{})
|
||||
paths[config.SpiderX] = struct{}{}
|
||||
maps.maps[uConn.ServerName] = paths
|
||||
}
|
||||
firstURL := string(prefix) + getPathLocked(paths)
|
||||
@@ -232,7 +232,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
||||
for _, m := range href.FindAllSubmatch(body, -1) {
|
||||
m[1] = bytes.TrimPrefix(m[1], prefix)
|
||||
if !bytes.Contains(m[1], dot) {
|
||||
paths[string(m[1])] = true
|
||||
paths[string(m[1])] = struct{}{}
|
||||
}
|
||||
}
|
||||
req.URL.Path = getPathLocked(paths)
|
||||
@@ -267,10 +267,10 @@ var (
|
||||
|
||||
var maps struct {
|
||||
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)))
|
||||
i := 0
|
||||
for s := range paths {
|
||||
|
@@ -5,13 +5,15 @@ import (
|
||||
"io"
|
||||
gonet "net"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/browser_dialer"
|
||||
"github.com/xtls/xray-core/transport/internet/websocket"
|
||||
)
|
||||
|
||||
// implements splithttp.DialerClient in terms of browser dialer
|
||||
// has no fields because everything is global state :O)
|
||||
type BrowserDialerClient struct{}
|
||||
// BrowserDialerClient implements splithttp.DialerClient in terms of browser dialer
|
||||
type BrowserDialerClient struct {
|
||||
transportConfig *Config
|
||||
}
|
||||
|
||||
func (c *BrowserDialerClient) IsClosed() bool {
|
||||
panic("not implemented yet")
|
||||
@@ -19,10 +21,10 @@ func (c *BrowserDialerClient) IsClosed() bool {
|
||||
|
||||
func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (io.ReadCloser, gonet.Addr, gonet.Addr, error) {
|
||||
if body != nil {
|
||||
panic("not implemented yet")
|
||||
return nil, nil, nil, errors.New("bidirectional streaming for browser dialer not implemented yet")
|
||||
}
|
||||
|
||||
conn, err := browser_dialer.DialGet(url)
|
||||
conn, err := browser_dialer.DialGet(url, c.transportConfig.GetRequestHeader(url))
|
||||
dummyAddr := &gonet.IPAddr{}
|
||||
if err != nil {
|
||||
return nil, dummyAddr, dummyAddr, err
|
||||
@@ -37,7 +39,7 @@ func (c *BrowserDialerClient) PostPacket(ctx context.Context, url string, body i
|
||||
return err
|
||||
}
|
||||
|
||||
err = browser_dialer.DialPost(url, bytes)
|
||||
err = browser_dialer.DialPost(url, c.transportConfig.GetRequestHeader(url), bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -55,12 +55,12 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
|
||||
},
|
||||
})
|
||||
|
||||
method := "GET"
|
||||
method := "GET" // stream-down
|
||||
if body != nil {
|
||||
method = "POST"
|
||||
method = "POST" // stream-up/one
|
||||
}
|
||||
req, _ := http.NewRequestWithContext(ctx, method, url, body)
|
||||
req.Header = c.transportConfig.GetRequestHeader()
|
||||
req, _ := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body)
|
||||
req.Header = c.transportConfig.GetRequestHeader(url)
|
||||
if method == "POST" && !c.transportConfig.NoGRPCHeader {
|
||||
req.Header.Set("Content-Type", "application/grpc")
|
||||
}
|
||||
@@ -69,17 +69,20 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
|
||||
go func() {
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
errors.LogInfoInner(ctx, err, "failed to "+method+" "+url)
|
||||
if !uploadOnly { // stream-down is enough
|
||||
c.closed = true
|
||||
errors.LogInfoInner(ctx, err, "failed to "+method+" "+url)
|
||||
}
|
||||
gotConn.Close()
|
||||
wrc.Close()
|
||||
return
|
||||
}
|
||||
if resp.StatusCode != 200 && !uploadOnly {
|
||||
// c.closed = true
|
||||
errors.LogInfo(ctx, "unexpected status ", resp.StatusCode)
|
||||
}
|
||||
if resp.StatusCode != 200 || uploadOnly {
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != 200 || uploadOnly { // stream-up
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close() // if it is called immediately, the upload will be interrupted also
|
||||
wrc.Close()
|
||||
return
|
||||
}
|
||||
@@ -91,23 +94,24 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
|
||||
}
|
||||
|
||||
func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body io.Reader, contentLength int64) error {
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, body)
|
||||
req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), "POST", url, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.ContentLength = contentLength
|
||||
req.Header = c.transportConfig.GetRequestHeader()
|
||||
req.Header = c.transportConfig.GetRequestHeader(url)
|
||||
|
||||
if c.httpVersion != "1.1" {
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
c.closed = true
|
||||
return err
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
// c.closed = true
|
||||
return errors.New("bad status code:", resp.Status)
|
||||
}
|
||||
} else {
|
||||
@@ -139,11 +143,12 @@ func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body i
|
||||
if h1UploadConn.UnreadedResponsesCount > 0 {
|
||||
resp, err := http.ReadResponse(h1UploadConn.RespBufReader, req)
|
||||
if err != nil {
|
||||
c.closed = true
|
||||
return fmt.Errorf("error while reading response: %s", err.Error())
|
||||
}
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
// c.closed = true
|
||||
// resp.Body.Close() // I'm not sure
|
||||
return fmt.Errorf("got non-200 error response code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
@@ -33,24 +34,31 @@ func (c *Config) GetNormalizedQuery() string {
|
||||
query = pathAndQuery[1]
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
query += "&"
|
||||
}
|
||||
|
||||
paddingLen := c.GetNormalizedXPaddingBytes().rand()
|
||||
if paddingLen > 0 {
|
||||
query += "x_padding=" + strings.Repeat("0", int(paddingLen))
|
||||
}
|
||||
/*
|
||||
if query != "" {
|
||||
query += "&"
|
||||
}
|
||||
query += "x_version=" + core.Version()
|
||||
*/
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func (c *Config) GetRequestHeader() http.Header {
|
||||
func (c *Config) GetRequestHeader(rawURL string) http.Header {
|
||||
header := http.Header{}
|
||||
for k, v := range c.Headers {
|
||||
header.Add(k, v)
|
||||
}
|
||||
|
||||
u, _ := url.Parse(rawURL)
|
||||
// https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B
|
||||
// h2's HPACK Header Compression feature employs a huffman encoding using a static table.
|
||||
// 'X' is assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire.
|
||||
// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2
|
||||
// h3's similar QPACK feature uses the same huffman table.
|
||||
u.RawQuery = "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand()))
|
||||
header.Set("Referer", u.String())
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
@@ -58,10 +66,8 @@ func (c *Config) WriteResponseHeader(writer http.ResponseWriter) {
|
||||
// CORS headers for the browser dialer
|
||||
writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
writer.Header().Set("Access-Control-Allow-Methods", "GET, POST")
|
||||
paddingLen := c.GetNormalizedXPaddingBytes().rand()
|
||||
if paddingLen > 0 {
|
||||
writer.Header().Set("X-Padding", strings.Repeat("0", int(paddingLen)))
|
||||
}
|
||||
// writer.Header().Set("X-Version", core.Version())
|
||||
writer.Header().Set("X-Padding", strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand())))
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedXPaddingBytes() RangeConfig {
|
||||
@@ -105,6 +111,17 @@ func (c *Config) GetNormalizedScMaxBufferedPosts() int {
|
||||
return int(c.ScMaxBufferedPosts)
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedScStreamUpServerSecs() RangeConfig {
|
||||
if c.ScStreamUpServerSecs == nil || c.ScStreamUpServerSecs.To == 0 {
|
||||
return RangeConfig{
|
||||
From: 20,
|
||||
To: 80,
|
||||
}
|
||||
}
|
||||
|
||||
return *c.ScMinPostsIntervalMs
|
||||
}
|
||||
|
||||
func (m *XmuxConfig) GetNormalizedMaxConcurrency() RangeConfig {
|
||||
if m.MaxConcurrency == nil {
|
||||
return RangeConfig{
|
||||
|
@@ -174,8 +174,9 @@ type Config struct {
|
||||
ScMaxEachPostBytes *RangeConfig `protobuf:"bytes,8,opt,name=scMaxEachPostBytes,proto3" json:"scMaxEachPostBytes,omitempty"`
|
||||
ScMinPostsIntervalMs *RangeConfig `protobuf:"bytes,9,opt,name=scMinPostsIntervalMs,proto3" json:"scMinPostsIntervalMs,omitempty"`
|
||||
ScMaxBufferedPosts int64 `protobuf:"varint,10,opt,name=scMaxBufferedPosts,proto3" json:"scMaxBufferedPosts,omitempty"`
|
||||
Xmux *XmuxConfig `protobuf:"bytes,11,opt,name=xmux,proto3" json:"xmux,omitempty"`
|
||||
DownloadSettings *internet.StreamConfig `protobuf:"bytes,12,opt,name=downloadSettings,proto3" json:"downloadSettings,omitempty"`
|
||||
ScStreamUpServerSecs *RangeConfig `protobuf:"bytes,11,opt,name=scStreamUpServerSecs,proto3" json:"scStreamUpServerSecs,omitempty"`
|
||||
Xmux *XmuxConfig `protobuf:"bytes,12,opt,name=xmux,proto3" json:"xmux,omitempty"`
|
||||
DownloadSettings *internet.StreamConfig `protobuf:"bytes,13,opt,name=downloadSettings,proto3" json:"downloadSettings,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -278,6 +279,13 @@ func (x *Config) GetScMaxBufferedPosts() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetScStreamUpServerSecs() *RangeConfig {
|
||||
if x != nil {
|
||||
return x.ScStreamUpServerSecs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetXmux() *XmuxConfig {
|
||||
if x != nil {
|
||||
return x.Xmux
|
||||
@@ -336,7 +344,7 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
|
||||
0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63,
|
||||
0x73, 0x12, 0x2a, 0x0a, 0x10, 0x68, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50,
|
||||
0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x68, 0x4b, 0x65,
|
||||
0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xf8, 0x05,
|
||||
0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xdc, 0x06,
|
||||
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||
@@ -371,29 +379,35 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
|
||||
0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42,
|
||||
0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x04, 0x78, 0x6d, 0x75, 0x78, 0x18, 0x0b,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x62, 0x0a, 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65,
|
||||
0x61, 0x6d, 0x55, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x63, 0x73, 0x18, 0x0b,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73,
|
||||
0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x58, 0x6d, 0x75, 0x78, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x04, 0x78, 0x6d, 0x75, 0x78, 0x12, 0x51, 0x0a, 0x10, 0x64, 0x6f, 0x77,
|
||||
0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0c, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74,
|
||||
0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x64, 0x6f, 0x77, 0x6e,
|
||||
0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x3a, 0x0a, 0x0c,
|
||||
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x85, 0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d,
|
||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
|
||||
0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02, 0x21, 0x58,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74, 0x74, 0x70,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x70,
|
||||
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x63, 0x73, 0x12, 0x41, 0x0a, 0x04, 0x78, 0x6d,
|
||||
0x75, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x58, 0x6d, 0x75,
|
||||
0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x04, 0x78, 0x6d, 0x75, 0x78, 0x12, 0x51, 0x0a,
|
||||
0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
||||
0x74, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||
0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x85, 0x01, 0x0a,
|
||||
0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c,
|
||||
0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70,
|
||||
0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74,
|
||||
0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -426,13 +440,14 @@ var file_transport_internet_splithttp_config_proto_depIdxs = []int32{
|
||||
0, // 6: xray.transport.internet.splithttp.Config.xPaddingBytes:type_name -> xray.transport.internet.splithttp.RangeConfig
|
||||
0, // 7: xray.transport.internet.splithttp.Config.scMaxEachPostBytes:type_name -> xray.transport.internet.splithttp.RangeConfig
|
||||
0, // 8: xray.transport.internet.splithttp.Config.scMinPostsIntervalMs:type_name -> xray.transport.internet.splithttp.RangeConfig
|
||||
1, // 9: xray.transport.internet.splithttp.Config.xmux:type_name -> xray.transport.internet.splithttp.XmuxConfig
|
||||
4, // 10: xray.transport.internet.splithttp.Config.downloadSettings:type_name -> xray.transport.internet.StreamConfig
|
||||
11, // [11:11] is the sub-list for method output_type
|
||||
11, // [11:11] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
0, // 9: xray.transport.internet.splithttp.Config.scStreamUpServerSecs:type_name -> xray.transport.internet.splithttp.RangeConfig
|
||||
1, // 10: xray.transport.internet.splithttp.Config.xmux:type_name -> xray.transport.internet.splithttp.XmuxConfig
|
||||
4, // 11: xray.transport.internet.splithttp.Config.downloadSettings:type_name -> xray.transport.internet.StreamConfig
|
||||
12, // [12:12] is the sub-list for method output_type
|
||||
12, // [12:12] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_splithttp_config_proto_init() }
|
||||
|
@@ -33,6 +33,7 @@ message Config {
|
||||
RangeConfig scMaxEachPostBytes = 8;
|
||||
RangeConfig scMinPostsIntervalMs = 9;
|
||||
int64 scMaxBufferedPosts = 10;
|
||||
XmuxConfig xmux = 11;
|
||||
xray.transport.internet.StreamConfig downloadSettings = 12;
|
||||
RangeConfig scStreamUpServerSecs = 11;
|
||||
XmuxConfig xmux = 12;
|
||||
xray.transport.internet.StreamConfig downloadSettings = 13;
|
||||
}
|
||||
|
@@ -13,8 +13,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/quic-go"
|
||||
"github.com/xtls/quic-go/http3"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -53,8 +53,8 @@ var (
|
||||
func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (DialerClient, *XmuxClient) {
|
||||
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
||||
|
||||
if browser_dialer.HasBrowserDialer() && realityConfig != nil {
|
||||
return &BrowserDialerClient{}, nil
|
||||
if browser_dialer.HasBrowserDialer() && realityConfig == nil {
|
||||
return &BrowserDialerClient{transportConfig: streamSettings.ProtocolSettings.(*Config)}, nil
|
||||
}
|
||||
|
||||
globalDialerAccess.Lock()
|
||||
@@ -367,19 +367,22 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
},
|
||||
}
|
||||
|
||||
var err error
|
||||
if mode == "stream-one" {
|
||||
requestURL.Path = transportConfiguration.GetNormalizedPath()
|
||||
if xmuxClient != nil {
|
||||
xmuxClient.LeftRequests.Add(-1)
|
||||
}
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, _ = httpClient.OpenStream(context.WithoutCancel(ctx), requestURL.String(), reader, false)
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient.OpenStream(ctx, requestURL.String(), reader, false)
|
||||
if err != nil { // browser dialer only
|
||||
return nil, err
|
||||
}
|
||||
return stat.Connection(&conn), nil
|
||||
} else { // stream-down
|
||||
var err error
|
||||
if xmuxClient2 != nil {
|
||||
xmuxClient2.LeftRequests.Add(-1)
|
||||
}
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(context.WithoutCancel(ctx), requestURL2.String(), nil, false)
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(ctx, requestURL2.String(), nil, false)
|
||||
if err != nil { // browser dialer only
|
||||
return nil, err
|
||||
}
|
||||
@@ -388,7 +391,10 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
if xmuxClient != nil {
|
||||
xmuxClient.LeftRequests.Add(-1)
|
||||
}
|
||||
httpClient.OpenStream(ctx, requestURL.String(), reader, true)
|
||||
_, _, _, err = httpClient.OpenStream(ctx, requestURL.String(), reader, true)
|
||||
if err != nil { // browser dialer only
|
||||
return nil, err
|
||||
}
|
||||
return stat.Connection(&conn), nil
|
||||
}
|
||||
|
||||
@@ -428,8 +434,6 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
// can reassign Path (potentially concurrently)
|
||||
url := requestURL
|
||||
url.Path += "/" + strconv.FormatInt(seq, 10)
|
||||
// reassign query to get different padding
|
||||
url.RawQuery = transportConfiguration.GetNormalizedQuery()
|
||||
|
||||
seq += 1
|
||||
|
||||
@@ -454,7 +458,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
|
||||
go func() {
|
||||
err := httpClient.PostPacket(
|
||||
context.WithoutCancel(ctx),
|
||||
ctx,
|
||||
url.String(),
|
||||
&buf.MultiBufferContainer{MultiBuffer: chunk},
|
||||
int64(chunk.Len()),
|
||||
|
@@ -1,18 +1,19 @@
|
||||
package splithttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
gotls "crypto/tls"
|
||||
"io"
|
||||
gonet "net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/quic-go"
|
||||
"github.com/xtls/quic-go/http3"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
goreality "github.com/xtls/reality"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -22,7 +23,7 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/reality"
|
||||
"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"
|
||||
)
|
||||
@@ -34,7 +35,7 @@ type requestHandler struct {
|
||||
ln *Listener
|
||||
sessionMu *sync.Mutex
|
||||
sessions sync.Map
|
||||
localAddr gonet.TCPAddr
|
||||
localAddr net.Addr
|
||||
}
|
||||
|
||||
type httpSession struct {
|
||||
@@ -102,10 +103,29 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
|
||||
h.config.WriteResponseHeader(writer)
|
||||
|
||||
/*
|
||||
clientVer := []int{0, 0, 0}
|
||||
x_version := strings.Split(request.URL.Query().Get("x_version"), ".")
|
||||
for j := 0; j < 3 && len(x_version) > j; j++ {
|
||||
clientVer[j], _ = strconv.Atoi(x_version[j])
|
||||
}
|
||||
*/
|
||||
|
||||
validRange := h.config.GetNormalizedXPaddingBytes()
|
||||
x_padding := int32(len(request.URL.Query().Get("x_padding")))
|
||||
if validRange.To > 0 && (x_padding < validRange.From || x_padding > validRange.To) {
|
||||
errors.LogInfo(context.Background(), "invalid x_padding length:", x_padding)
|
||||
paddingLength := 0
|
||||
|
||||
referrer := request.Header.Get("Referer")
|
||||
if referrer != "" {
|
||||
if referrerURL, err := url.Parse(referrer); err == nil {
|
||||
// Browser dialer cannot control the host part of referrer header, so only check the query
|
||||
paddingLength = len(referrerURL.Query().Get("x_padding"))
|
||||
}
|
||||
} else {
|
||||
paddingLength = len(request.URL.Query().Get("x_padding"))
|
||||
}
|
||||
|
||||
if int32(paddingLength) < validRange.From || int32(paddingLength) > validRange.To {
|
||||
errors.LogInfo(context.Background(), "invalid x_padding length:", int32(paddingLength))
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -123,14 +143,25 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
|
||||
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 {
|
||||
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() {
|
||||
remoteAddr = &net.TCPAddr{
|
||||
IP: forwardedAddrs[0].IP(),
|
||||
Port: int(0),
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +171,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To)
|
||||
|
||||
if request.Method == "POST" && sessionId != "" {
|
||||
if request.Method == "POST" && sessionId != "" { // stream-up, packet-up
|
||||
seq := ""
|
||||
if len(subpath) > 1 {
|
||||
seq = subpath[1]
|
||||
@@ -152,16 +183,42 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
uploadDone := done.New()
|
||||
err = currentSession.uploadQueue.Push(Packet{
|
||||
Reader: request.Body,
|
||||
Reader: &httpRequestBodyReader{
|
||||
requestReader: request.Body,
|
||||
uploadDone: uploadDone,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to upload (PushReader)")
|
||||
writer.WriteHeader(http.StatusConflict)
|
||||
} else {
|
||||
writer.Header().Set("X-Accel-Buffering", "no")
|
||||
writer.Header().Set("Cache-Control", "no-store")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
<-request.Context().Done()
|
||||
scStreamUpServerSecs := h.config.GetNormalizedScStreamUpServerSecs()
|
||||
if referrer != "" && scStreamUpServerSecs.To > 0 {
|
||||
go func() {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
for {
|
||||
_, err := writer.Write(bytes.Repeat([]byte{'X'}, int(h.config.GetNormalizedXPaddingBytes().rand())))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
writer.(http.Flusher).Flush()
|
||||
time.Sleep(time.Duration(scStreamUpServerSecs.rand()) * time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
select {
|
||||
case <-request.Context().Done():
|
||||
case <-uploadDone.Wait():
|
||||
}
|
||||
}
|
||||
uploadDone.Close()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -171,10 +228,10 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
return
|
||||
}
|
||||
|
||||
payload, err := io.ReadAll(request.Body)
|
||||
payload, err := io.ReadAll(io.LimitReader(request.Body, int64(scMaxEachPostBytes)+1))
|
||||
|
||||
if len(payload) > scMaxEachPostBytes {
|
||||
errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request had size ", len(payload), ". Adjust scMaxEachPostBytes on the server to be at least as large as client.")
|
||||
errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request size exceed it. Adjust scMaxEachPostBytes on the server to be at least as large as client.")
|
||||
writer.WriteHeader(http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
@@ -204,7 +261,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
|
||||
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")
|
||||
@@ -242,9 +299,10 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
responseFlusher: responseFlusher,
|
||||
},
|
||||
reader: request.Body,
|
||||
localAddr: h.localAddr,
|
||||
remoteAddr: remoteAddr,
|
||||
}
|
||||
if sessionId != "" {
|
||||
if sessionId != "" { // if not stream-one
|
||||
conn.reader = currentSession.uploadQueue
|
||||
}
|
||||
|
||||
@@ -263,6 +321,20 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
}
|
||||
|
||||
type httpRequestBodyReader struct {
|
||||
requestReader io.ReadCloser
|
||||
uploadDone *done.Instance
|
||||
}
|
||||
|
||||
func (c *httpRequestBodyReader) Read(b []byte) (int, error) {
|
||||
return c.requestReader.Read(b)
|
||||
}
|
||||
|
||||
func (c *httpRequestBodyReader) Close() error {
|
||||
defer c.uploadDone.Close()
|
||||
return c.requestReader.Close()
|
||||
}
|
||||
|
||||
type httpResponseBodyWriter struct {
|
||||
sync.Mutex
|
||||
responseWriter http.ResponseWriter
|
||||
@@ -301,34 +373,30 @@ type Listener struct {
|
||||
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{
|
||||
addConn: addConn,
|
||||
}
|
||||
shSettings := streamSettings.ProtocolSettings.(*Config)
|
||||
l.config = shSettings
|
||||
l.config = streamSettings.ProtocolSettings.(*Config)
|
||||
if l.config != nil {
|
||||
if streamSettings.SocketSettings == nil {
|
||||
streamSettings.SocketSettings = &internet.SocketConfig{}
|
||||
}
|
||||
}
|
||||
var listener net.Listener
|
||||
var err error
|
||||
var localAddr = gonet.TCPAddr{}
|
||||
handler := &requestHandler{
|
||||
config: shSettings,
|
||||
host: shSettings.Host,
|
||||
path: shSettings.GetNormalizedPath(),
|
||||
config: l.config,
|
||||
host: l.config.Host,
|
||||
path: l.config.GetNormalizedPath(),
|
||||
ln: l,
|
||||
sessionMu: &sync.Mutex{},
|
||||
sessions: sync.Map{},
|
||||
localAddr: localAddr,
|
||||
}
|
||||
tlsConfig := getTLSConfig(streamSettings)
|
||||
l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
|
||||
|
||||
var err error
|
||||
if port == net.Port(0) { // unix
|
||||
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
||||
l.listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
||||
Name: address.Domain(),
|
||||
Net: "unix",
|
||||
}, streamSettings.SocketSettings)
|
||||
@@ -344,27 +412,24 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
||||
if err != nil {
|
||||
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 {
|
||||
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)
|
||||
|
||||
handler.localAddr = l.h3listener.Addr()
|
||||
|
||||
l.h3server = &http3.Server{
|
||||
Handler: handler,
|
||||
}
|
||||
go func() {
|
||||
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
|
||||
localAddr = gonet.TCPAddr{
|
||||
IP: address.IP(),
|
||||
Port: int(port),
|
||||
}
|
||||
listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
|
||||
l.listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
|
||||
IP: address.IP(),
|
||||
Port: int(port),
|
||||
}, streamSettings.SocketSettings)
|
||||
@@ -375,29 +440,27 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
||||
}
|
||||
|
||||
// tcp/unix (h1/h2)
|
||||
if listener != nil {
|
||||
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||
if l.listener != nil {
|
||||
if config := tls.ConfigFromStreamSettings(streamSettings); config != 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 {
|
||||
listener = goreality.NewListener(listener, config.GetREALITYConfig())
|
||||
l.listener = goreality.NewListener(l.listener, config.GetREALITYConfig())
|
||||
}
|
||||
|
||||
handler.localAddr = l.listener.Addr()
|
||||
|
||||
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
||||
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
||||
l.listener = listener
|
||||
l.server = http.Server{
|
||||
Handler: h2cHandler,
|
||||
Handler: h2c.NewHandler(handler, &http2.Server{}),
|
||||
ReadHeaderTimeout: time.Second * 4,
|
||||
MaxHeaderBytes: 8192,
|
||||
}
|
||||
|
||||
go func() {
|
||||
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")
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -427,13 +490,13 @@ func (ln *Listener) Close() error {
|
||||
}
|
||||
return errors.New("listener does not have an HTTP/3 server or a net.listener")
|
||||
}
|
||||
func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *tls.Config {
|
||||
config := v2tls.ConfigFromStreamSettings(streamSettings)
|
||||
func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *gotls.Config {
|
||||
config := tls.ConfigFromStreamSettings(streamSettings)
|
||||
if config == nil {
|
||||
return &tls.Config{}
|
||||
return &gotls.Config{}
|
||||
}
|
||||
return config.GetTLSConfig()
|
||||
}
|
||||
func init() {
|
||||
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
|
||||
common.Must(internet.RegisterTransportListener(protocolName, ListenXH))
|
||||
}
|
||||
|
@@ -26,9 +26,9 @@ import (
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
func Test_listenSHAndDial(t *testing.T) {
|
||||
func Test_ListenXHAndDial(t *testing.T) {
|
||||
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",
|
||||
ProtocolSettings: &Config{
|
||||
Path: "/sh",
|
||||
@@ -85,7 +85,7 @@ func Test_listenSHAndDial(t *testing.T) {
|
||||
|
||||
func TestDialWithRemoteAddr(t *testing.T) {
|
||||
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",
|
||||
ProtocolSettings: &Config{
|
||||
Path: "sh",
|
||||
@@ -125,7 +125,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
||||
common.Must(listen.Close())
|
||||
}
|
||||
|
||||
func Test_listenSHAndDial_TLS(t *testing.T) {
|
||||
func Test_ListenXHAndDial_TLS(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
return
|
||||
}
|
||||
@@ -145,7 +145,7 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
|
||||
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() {
|
||||
defer conn.Close()
|
||||
|
||||
@@ -180,7 +180,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" {
|
||||
return
|
||||
}
|
||||
@@ -193,7 +193,7 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
||||
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() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
@@ -227,7 +227,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" {
|
||||
return
|
||||
}
|
||||
@@ -250,7 +250,7 @@ func Test_listenSHAndDial_QUIC(t *testing.T) {
|
||||
}
|
||||
|
||||
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() {
|
||||
defer conn.Close()
|
||||
|
||||
@@ -309,11 +309,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()
|
||||
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",
|
||||
ProtocolSettings: &Config{
|
||||
Path: "/sh",
|
||||
@@ -373,7 +373,7 @@ func Test_listenSHAndDial_Unix(t *testing.T) {
|
||||
|
||||
func Test_queryString(t *testing.T) {
|
||||
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",
|
||||
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
|
||||
@@ -431,7 +431,7 @@ func Test_maxUpload(t *testing.T) {
|
||||
}
|
||||
|
||||
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) {
|
||||
defer c.Close()
|
||||
var b [10240]byte
|
||||
|
@@ -21,19 +21,6 @@ type DefaultListener struct {
|
||||
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 {
|
||||
return func(network, address string, c syscall.RawConn) error {
|
||||
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 listenUDSWrapper struct {
|
||||
net.Listener
|
||||
locker *FileLocker
|
||||
}
|
||||
|
||||
func (l *listenUDSWrapper) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UDSWrapperConn{Conn: conn}, nil
|
||||
}
|
||||
|
||||
func (l *listenUDSWrapper) Close() error {
|
||||
if l.locker != nil {
|
||||
l.locker.Release()
|
||||
l.locker = nil
|
||||
}
|
||||
return l.Listener.Close()
|
||||
}
|
||||
|
||||
type UDSWrapperConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (conn *UDSWrapperConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: []byte{0, 0, 0, 0},
|
||||
}
|
||||
}
|
||||
|
||||
func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (l net.Listener, err error) {
|
||||
var lc net.ListenConfig
|
||||
var network, address string
|
||||
@@ -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) {
|
||||
if err != nil {
|
||||
locker.Release()
|
||||
return l, err
|
||||
return nil, err
|
||||
}
|
||||
l = &combinedListener{Listener: l, locker: locker}
|
||||
l = &listenUDSWrapper{Listener: l, locker: locker}
|
||||
if filePerm == 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(l, err)
|
||||
if sockopt != nil && sockopt.AcceptProxyProtocol {
|
||||
l, err = callback(lc.Listen(ctx, network, address))
|
||||
if err == nil && sockopt != nil && sockopt.AcceptProxyProtocol {
|
||||
policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
|
||||
l = &proxyproto.Listener{Listener: l, Policy: policyFunc}
|
||||
}
|
||||
|
@@ -2,16 +2,23 @@ package tcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/reality"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
func IsFromMitm(str string) bool {
|
||||
return strings.ToLower(str) == "frommitm"
|
||||
}
|
||||
|
||||
// Dial dials a new TCP connection to the given destination.
|
||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
||||
errors.LogInfo(ctx, "dialing TCP to ", dest)
|
||||
@@ -21,14 +28,63 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
}
|
||||
|
||||
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||
mitmServerName := session.MitmServerNameFromContext(ctx)
|
||||
mitmAlpn11 := session.MitmAlpn11FromContext(ctx)
|
||||
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
|
||||
if IsFromMitm(tlsConfig.ServerName) {
|
||||
tlsConfig.ServerName = mitmServerName
|
||||
}
|
||||
isFromMitmVerify := false
|
||||
if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertInNames) > 0 {
|
||||
for i, name := range r.VerifyPeerCertInNames {
|
||||
if IsFromMitm(name) {
|
||||
isFromMitmVerify = true
|
||||
r.VerifyPeerCertInNames[0], r.VerifyPeerCertInNames[i] = r.VerifyPeerCertInNames[i], r.VerifyPeerCertInNames[0]
|
||||
r.VerifyPeerCertInNames = r.VerifyPeerCertInNames[1:]
|
||||
after := mitmServerName
|
||||
for {
|
||||
if len(after) > 0 {
|
||||
r.VerifyPeerCertInNames = append(r.VerifyPeerCertInNames, after)
|
||||
}
|
||||
_, after, _ = strings.Cut(after, ".")
|
||||
if !strings.Contains(after, ".") {
|
||||
break
|
||||
}
|
||||
}
|
||||
slices.Reverse(r.VerifyPeerCertInNames)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
isFromMitmAlpn := len(tlsConfig.NextProtos) == 1 && IsFromMitm(tlsConfig.NextProtos[0])
|
||||
if isFromMitmAlpn {
|
||||
if mitmAlpn11 {
|
||||
tlsConfig.NextProtos[0] = "http/1.1"
|
||||
} else {
|
||||
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||
}
|
||||
}
|
||||
if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil {
|
||||
conn = tls.UClient(conn, tlsConfig, fingerprint)
|
||||
if err := conn.(*tls.UConn).HandshakeContext(ctx); err != nil {
|
||||
return nil, err
|
||||
if len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "http/1.1" { // allow manually specify
|
||||
err = conn.(*tls.UConn).WebsocketHandshakeContext(ctx)
|
||||
} else {
|
||||
err = conn.(*tls.UConn).HandshakeContext(ctx)
|
||||
}
|
||||
} else {
|
||||
conn = tls.Client(conn, tlsConfig)
|
||||
err = conn.(*tls.Conn).HandshakeContext(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
if isFromMitmVerify {
|
||||
return nil, errors.New("MITM freedom RAW TLS: failed to verify Domain Fronting certificate from " + mitmServerName).Base(err).AtWarning()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
negotiatedProtocol := conn.(tls.Interface).NegotiatedProtocol()
|
||||
if isFromMitmAlpn && !mitmAlpn11 && negotiatedProtocol != "h2" {
|
||||
conn.Close()
|
||||
return nil, errors.New("MITM freedom RAW TLS: unexpected Negotiated Protocol (" + negotiatedProtocol + ") with " + mitmServerName).AtWarning()
|
||||
}
|
||||
} else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||
if conn, err = reality.UClient(conn, config, ctx, dest); err != nil {
|
||||
|
@@ -4,10 +4,12 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -276,10 +278,35 @@ func (c *Config) parseServerName() string {
|
||||
return c.ServerName
|
||||
}
|
||||
|
||||
func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
if c.PinnedPeerCertificateChainSha256 != nil {
|
||||
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
if r.VerifyPeerCertInNames != nil {
|
||||
if len(r.VerifyPeerCertInNames) > 0 {
|
||||
certs := make([]*x509.Certificate, len(rawCerts))
|
||||
for i, asn1Data := range rawCerts {
|
||||
certs[i], _ = x509.ParseCertificate(asn1Data)
|
||||
}
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: r.RootCAs,
|
||||
CurrentTime: time.Now(),
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
for _, opts.DNSName = range r.VerifyPeerCertInNames {
|
||||
if _, err := certs[0].Verify(opts); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.PinnedPeerCertificateChainSha256 == nil {
|
||||
return errors.New("peer cert is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
if r.PinnedPeerCertificateChainSha256 != nil {
|
||||
hashValue := GenerateCertChainHash(rawCerts)
|
||||
for _, v := range c.PinnedPeerCertificateChainSha256 {
|
||||
for _, v := range r.PinnedPeerCertificateChainSha256 {
|
||||
if hmac.Equal(hashValue, v) {
|
||||
return nil
|
||||
}
|
||||
@@ -287,11 +314,11 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
|
||||
return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue))
|
||||
}
|
||||
|
||||
if c.PinnedPeerCertificatePublicKeySha256 != nil {
|
||||
if r.PinnedPeerCertificatePublicKeySha256 != nil {
|
||||
for _, v := range verifiedChains {
|
||||
for _, cert := range v {
|
||||
publicHash := GenerateCertPublicKeyHash(cert)
|
||||
for _, c := range c.PinnedPeerCertificatePublicKeySha256 {
|
||||
for _, c := range r.PinnedPeerCertificatePublicKeySha256 {
|
||||
if hmac.Equal(publicHash, c) {
|
||||
return nil
|
||||
}
|
||||
@@ -303,6 +330,17 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
|
||||
return nil
|
||||
}
|
||||
|
||||
type RandCarrier struct {
|
||||
RootCAs *x509.CertPool
|
||||
VerifyPeerCertInNames []string
|
||||
PinnedPeerCertificateChainSha256 [][]byte
|
||||
PinnedPeerCertificatePublicKeySha256 [][]byte
|
||||
}
|
||||
|
||||
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
||||
return rand.Read(p)
|
||||
}
|
||||
|
||||
// GetTLSConfig converts this Config into tls.Config.
|
||||
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
root, err := c.getCertPool()
|
||||
@@ -320,13 +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{
|
||||
Rand: randCarrier,
|
||||
ClientSessionCache: globalSessionCache,
|
||||
RootCAs: root,
|
||||
InsecureSkipVerify: c.AllowInsecure,
|
||||
NextProtos: c.NextProtocol,
|
||||
NextProtos: slices.Clone(c.NextProtocol),
|
||||
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 {
|
||||
|
@@ -202,19 +202,21 @@ type Config struct {
|
||||
// TLS Client Hello fingerprint (uTLS).
|
||||
Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
|
||||
RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"`
|
||||
// @Document A pinned certificate chain sha256 hash.
|
||||
// @Document If the server's hash does not match this value, the connection will be aborted.
|
||||
// @Document This value replace allow_insecure.
|
||||
// @Document Some certificate chain sha256 hashes.
|
||||
// @Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
|
||||
// @Critical
|
||||
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 If the server's public key hash does not match this value, the connection will be aborted.
|
||||
// @Document This value replace allow_insecure.
|
||||
// @Document Some certificate public key sha256 hashes.
|
||||
// @Document After normal validation (required), if the verified cert's public key hash does not match any of these values, the connection will be aborted.
|
||||
// @Critical
|
||||
PinnedPeerCertificatePublicKeySha256 [][]byte `protobuf:"bytes,14,rep,name=pinned_peer_certificate_public_key_sha256,json=pinnedPeerCertificatePublicKeySha256,proto3" json:"pinned_peer_certificate_public_key_sha256,omitempty"`
|
||||
MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
|
||||
// Lists of string as CurvePreferences values.
|
||||
CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"`
|
||||
// @Document Replaces server_name to verify the peer cert.
|
||||
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||
// @Critical
|
||||
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -352,6 +354,13 @@ func (x *Config) GetCurvePreferences() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetVerifyPeerCertInNames() []string {
|
||||
if x != nil {
|
||||
return x.VerifyPeerCertInNames
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
@@ -383,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,
|
||||
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
||||
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
||||
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xe0, 0x05, 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,
|
||||
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,
|
||||
@@ -429,15 +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,
|
||||
0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
||||
0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76, 0x65,
|
||||
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63,
|
||||
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01,
|
||||
0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74,
|
||||
0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x76,
|
||||
0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
|
||||
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15,
|
||||
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
|
||||
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -69,16 +69,14 @@ message Config {
|
||||
|
||||
bool reject_unknown_sni = 12;
|
||||
|
||||
/* @Document A pinned certificate chain sha256 hash.
|
||||
@Document If the server's hash does not match this value, the connection will be aborted.
|
||||
@Document This value replace allow_insecure.
|
||||
/* @Document Some certificate chain sha256 hashes.
|
||||
@Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
|
||||
@Critical
|
||||
*/
|
||||
repeated bytes pinned_peer_certificate_chain_sha256 = 13;
|
||||
|
||||
/* @Document A pinned certificate public key sha256 hash.
|
||||
@Document If the server's public key hash does not match this value, the connection will be aborted.
|
||||
@Document This value replace allow_insecure.
|
||||
/* @Document Some certificate public key sha256 hashes.
|
||||
@Document After normal validation (required), if the verified cert's public key hash does not match any of these values, the connection will be aborted.
|
||||
@Critical
|
||||
*/
|
||||
repeated bytes pinned_peer_certificate_public_key_sha256 = 14;
|
||||
@@ -87,4 +85,10 @@ message Config {
|
||||
|
||||
// Lists of string as CurvePreferences values.
|
||||
repeated string curve_preferences = 16;
|
||||
|
||||
/* @Document Replaces server_name to verify the peer cert.
|
||||
@Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||
@Critical
|
||||
*/
|
||||
repeated string verify_peer_cert_in_names = 17;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ type Interface interface {
|
||||
net.Conn
|
||||
HandshakeContext(ctx context.Context) error
|
||||
VerifyHostname(host string) error
|
||||
HandshakeContextServerName(ctx context.Context) string
|
||||
NegotiatedProtocol() string
|
||||
}
|
||||
|
||||
@@ -43,15 +44,11 @@ func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
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 {
|
||||
return nil
|
||||
return ""
|
||||
}
|
||||
state := c.ConnectionState()
|
||||
if state.ServerName == "" {
|
||||
return nil
|
||||
}
|
||||
return net.ParseAddress(state.ServerName)
|
||||
return c.ConnectionState().ServerName
|
||||
}
|
||||
|
||||
func (c *Conn) NegotiatedProtocol() string {
|
||||
@@ -85,15 +82,11 @@ func (c *UConn) Close() error {
|
||||
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 {
|
||||
return nil
|
||||
return ""
|
||||
}
|
||||
state := c.ConnectionState()
|
||||
if state.ServerName == "" {
|
||||
return nil
|
||||
}
|
||||
return net.ParseAddress(state.ServerName)
|
||||
return c.ConnectionState().ServerName
|
||||
}
|
||||
|
||||
// WebsocketHandshake basically calls UConn.Handshake inside it but it will only send
|
||||
@@ -135,6 +128,7 @@ func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) ne
|
||||
|
||||
func copyConfig(c *tls.Config) *utls.Config {
|
||||
return &utls.Config{
|
||||
Rand: c.Rand,
|
||||
RootCAs: c.RootCAs,
|
||||
ServerName: c.ServerName,
|
||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||
|
Reference in New Issue
Block a user