mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 09:36:49 +08:00
Compare commits
53 Commits
v25.1.30
...
v1.250306.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2cba2c4d59 | ||
![]() |
306fa51475 | ||
![]() |
6d6f1c6967 | ||
![]() |
152959824f | ||
![]() |
a977b6357e | ||
![]() |
16eee1b89c | ||
![]() |
dde0a4f272 | ||
![]() |
e15dff94b5 | ||
![]() |
e466b0497c | ||
![]() |
b9cb93d3c2 | ||
![]() |
8d46f7e14c | ||
![]() |
4b616f5cd0 | ||
![]() |
06b4a7ce4d | ||
![]() |
4c12e1686b | ||
![]() |
225d151cd3 | ||
![]() |
d451078e72 | ||
![]() |
ce2384cccc | ||
![]() |
be43f66b63 | ||
![]() |
71a6d89c23 | ||
![]() |
89792aee9d | ||
![]() |
b786a50aee | ||
![]() |
b38a53e629 | ||
![]() |
52381a3c03 | ||
![]() |
4b01eb4398 | ||
![]() |
c5de08bea6 | ||
![]() |
8cb63db6c0 | ||
![]() |
eef74b2c7d | ||
![]() |
a1714cc4ce | ||
![]() |
958b13ebb5 | ||
![]() |
22c50a70c6 | ||
![]() |
94c7970fd6 | ||
![]() |
a71762b5da | ||
![]() |
5033cbceea | ||
![]() |
dcd7e92c45 | ||
![]() |
2d7ca4a6a6 | ||
![]() |
925a985cc0 | ||
![]() |
613c63b165 | ||
![]() |
d4c7cd02fd | ||
![]() |
db5f18b98c | ||
![]() |
c81d8e488a | ||
![]() |
1d9e6bc2f3 | ||
![]() |
ae327eb7e6 | ||
![]() |
e893fa1828 | ||
![]() |
1982c2366e | ||
![]() |
117de1fd3c | ||
![]() |
07c35ed52a | ||
![]() |
e17c068821 | ||
![]() |
88d40d6367 | ||
![]() |
527caa3711 | ||
![]() |
c6a31f457c | ||
![]() |
9b7841178a | ||
![]() |
480c7d7db7 | ||
![]() |
c2f6c89987 |
27
.github/workflows/release-win7.yml
vendored
27
.github/workflows/release-win7.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: Build and Release for Windows 7
|
||||
|
||||
# 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:
|
||||
@@ -37,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 }}
|
||||
@@ -46,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
|
||||
@@ -65,8 +61,13 @@ 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 Geodat Cache
|
||||
uses: actions/cache/restore@v4
|
||||
|
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: Build and Release
|
||||
|
||||
# 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:
|
||||
@@ -129,8 +123,22 @@ 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 Geodat Cache
|
||||
uses: actions/cache/restore@v4
|
||||
|
@@ -1,4 +1,4 @@
|
||||
name: Timely assets update
|
||||
name: Scheduled assets update
|
||||
|
||||
# NOTE: This Github Actions is required by other actions, for preparing other packaging assets in a
|
||||
# routine manner, for example: GeoIP/GeoSite.
|
||||
@@ -8,19 +8,20 @@ name: Timely assets update
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Update assets on every hour (xx:30)
|
||||
- cron: '30 * * * *'
|
||||
# Update GeoData on every day (22:30 UTC)
|
||||
- cron: '30 22 * * *'
|
||||
push:
|
||||
# Prevent triggering update request storm
|
||||
paths:
|
||||
- ".github/workflows/hourly-prepare.yml"
|
||||
- ".github/workflows/scheduled-assets-update.yml"
|
||||
pull_request:
|
||||
# Prevent triggering update request storm
|
||||
paths:
|
||||
- ".github/workflows/hourly-prepare.yml"
|
||||
- ".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
|
||||
@@ -38,18 +39,18 @@ jobs:
|
||||
max_attempts: 60
|
||||
command: |
|
||||
[ -d 'resources' ] || mkdir resources
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
LIST=('Loyalsoldier v2ray-rules-dat geoip geoip' 'Loyalsoldier v2ray-rules-dat geosite geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
||||
FILE_NAME="${INFO[2]}.dat"
|
||||
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/v2fly/${INFO[0]}/release/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
HASH="$(curl -sL "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
|
||||
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 "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
|
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)
|
||||
@@ -81,6 +85,7 @@
|
||||
- [X-flutter](https://github.com/XTLS/X-flutter)
|
||||
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
|
||||
- iOS & macOS arm64
|
||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
|
||||
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
||||
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
||||
- macOS arm64 & x64
|
||||
@@ -122,25 +127,27 @@
|
||||
- [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases).
|
||||
- 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
|
||||
|
@@ -45,11 +45,13 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
|
||||
case strings.EqualFold(u.String(), "localhost"):
|
||||
return NewLocalNameServer(queryStrategy), nil
|
||||
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
|
||||
return NewDoHNameServer(u, dispatcher, queryStrategy, false)
|
||||
return NewDoHNameServer(u, queryStrategy, dispatcher, false), nil
|
||||
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
|
||||
return NewDoHNameServer(u, dispatcher, queryStrategy, true)
|
||||
return NewDoHNameServer(u, queryStrategy, dispatcher, true), nil
|
||||
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
|
||||
return NewDoHLocalNameServer(u, queryStrategy), nil
|
||||
return NewDoHNameServer(u, queryStrategy, nil, false), nil
|
||||
case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
|
||||
return NewDoHNameServer(u, queryStrategy, nil, true), nil
|
||||
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
|
||||
return NewQUICNameServer(u, queryStrategy)
|
||||
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
|
||||
|
@@ -8,10 +8,13 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -31,7 +34,6 @@ import (
|
||||
// which is compatible with traditional dns over udp(RFC1035),
|
||||
// thus most of the DOH implementation is copied from udpns.go
|
||||
type DoHNameServer struct {
|
||||
dispatcher routing.Dispatcher
|
||||
sync.RWMutex
|
||||
ips map[string]*record
|
||||
pub *pubsub.Service
|
||||
@@ -42,103 +44,18 @@ type DoHNameServer struct {
|
||||
queryStrategy QueryStrategy
|
||||
}
|
||||
|
||||
// NewDoHNameServer creates DOH server object for remote resolving.
|
||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy QueryStrategy, h2c bool) (*DoHNameServer, error) {
|
||||
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
|
||||
func NewDoHNameServer(url *url.URL, queryStrategy QueryStrategy, dispatcher routing.Dispatcher, h2c bool) *DoHNameServer {
|
||||
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
|
||||
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link, err := s.dispatcher.Dispatch(toDnsContext(ctx, s.dohURL), dest)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := common.ChainedClosable{}
|
||||
if cw, ok := link.Writer.(common.Closable); ok {
|
||||
cc = append(cc, cw)
|
||||
}
|
||||
if cr, ok := link.Reader.(common.Closable); ok {
|
||||
cc = append(cc, cr)
|
||||
}
|
||||
return cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(link.Writer),
|
||||
cnc.ConnectionOutputMulti(link.Reader),
|
||||
cnc.ConnectionOnClose(cc),
|
||||
), nil
|
||||
mode := "DOH"
|
||||
if dispatcher == nil {
|
||||
mode = "DOHL"
|
||||
}
|
||||
|
||||
s.httpClient = &http.Client{
|
||||
Timeout: time.Second * 180,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 30,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: dialContext,
|
||||
},
|
||||
}
|
||||
if h2c {
|
||||
s.httpClient.Transport = &http2.Transport{
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
return dialContext(ctx, network, addr)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// NewDoHLocalNameServer creates DOH client object for local resolving
|
||||
func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy) *DoHNameServer {
|
||||
url.Scheme = "https"
|
||||
s := baseDOHNameServer(url, "DOHL", queryStrategy)
|
||||
tr := &http.Transport{
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := internet.DialSystem(ctx, dest, nil)
|
||||
log.Record(&log.AccessMessage{
|
||||
From: "DNS",
|
||||
To: s.dohURL,
|
||||
Status: log.AccessAccepted,
|
||||
Detour: "local",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
},
|
||||
}
|
||||
s.httpClient = &http.Client{
|
||||
Timeout: time.Second * 180,
|
||||
Transport: tr,
|
||||
}
|
||||
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-HTTPS client for ", url.String())
|
||||
return s
|
||||
}
|
||||
|
||||
func baseDOHNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy) *DoHNameServer {
|
||||
errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c)
|
||||
s := &DoHNameServer{
|
||||
ips: make(map[string]*record),
|
||||
pub: pubsub.NewService(),
|
||||
name: prefix + "//" + url.Host,
|
||||
name: mode + "//" + url.Host,
|
||||
dohURL: url.String(),
|
||||
queryStrategy: queryStrategy,
|
||||
}
|
||||
@@ -146,6 +63,65 @@ func baseDOHNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy)
|
||||
Interval: time.Minute,
|
||||
Execute: s.Cleanup,
|
||||
}
|
||||
s.httpClient = &http.Client{
|
||||
Transport: &http2.Transport{
|
||||
IdleConnTimeout: net.ConnIdleTimeout,
|
||||
ReadIdleTimeout: net.ChromeH2KeepAlivePeriod,
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var conn net.Conn
|
||||
if dispatcher != nil {
|
||||
dnsCtx := toDnsContext(ctx, s.dohURL)
|
||||
if h2c {
|
||||
dnsCtx = session.ContextWithMitmAlpn11(dnsCtx, false) // for insurance
|
||||
dnsCtx = session.ContextWithMitmServerName(dnsCtx, url.Hostname())
|
||||
}
|
||||
link, err := dispatcher.Dispatch(dnsCtx, dest)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := common.ChainedClosable{}
|
||||
if cw, ok := link.Writer.(common.Closable); ok {
|
||||
cc = append(cc, cw)
|
||||
}
|
||||
if cr, ok := link.Reader.(common.Closable); ok {
|
||||
cc = append(cc, cr)
|
||||
}
|
||||
conn = cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(link.Writer),
|
||||
cnc.ConnectionOutputMulti(link.Reader),
|
||||
cnc.ConnectionOnClose(cc),
|
||||
)
|
||||
} else {
|
||||
log.Record(&log.AccessMessage{
|
||||
From: "DNS",
|
||||
To: s.dohURL,
|
||||
Status: log.AccessAccepted,
|
||||
Detour: "local",
|
||||
})
|
||||
conn, err = internet.DialSystem(ctx, dest, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if !h2c {
|
||||
conn = utls.UClient(conn, &utls.Config{ServerName: url.Hostname()}, utls.HelloChrome_Auto)
|
||||
if err := conn.(*utls.UConn).HandshakeContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -305,6 +281,8 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
||||
req.Header.Add("Accept", "application/dns-message")
|
||||
req.Header.Add("Content-Type", "application/dns-message")
|
||||
|
||||
req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000))))
|
||||
|
||||
hc := s.httpClient
|
||||
|
||||
resp, err := hc.Do(req.WithContext(ctx))
|
||||
|
@@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
|
||||
s := NewDoHNameServer(url, QueryStrategy_USE_IP, nil, false)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
@@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
|
||||
s := NewDoHNameServer(url, QueryStrategy_USE_IP, nil, false)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
@@ -62,7 +62,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP4)
|
||||
s := NewDoHNameServer(url, QueryStrategy_USE_IP4, nil, false)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
@@ -85,7 +85,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP6)
|
||||
s := NewDoHNameServer(url, QueryStrategy_USE_IP6, nil, false)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -325,6 +324,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
||||
if w.sniffingConfig != nil {
|
||||
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
||||
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
|
||||
}
|
||||
@@ -464,19 +464,8 @@ func (w *dsWorker) callback(conn stat.Connection) {
|
||||
WriteCounter: w.downlinkCounter,
|
||||
}
|
||||
}
|
||||
// For most of time, unix obviously have no source addr. But if we leave it empty, it will cause panic.
|
||||
// So we use gateway as source for log.
|
||||
// However, there are some special situations where a valid source address might be available.
|
||||
// Such as the source address parsed from X-Forwarded-For in websocket.
|
||||
// In that case, we keep it.
|
||||
var source net.Destination
|
||||
if !strings.Contains(conn.RemoteAddr().String(), "unix") {
|
||||
source = net.DestinationFromAddr(conn.RemoteAddr())
|
||||
} else {
|
||||
source = net.UnixDestination(w.address)
|
||||
}
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Source: source,
|
||||
Source: net.DestinationFromAddr(conn.RemoteAddr()),
|
||||
Gateway: net.UnixDestination(w.address),
|
||||
Tag: w.tag,
|
||||
Conn: conn,
|
||||
|
@@ -273,7 +273,16 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
if h.senderSettings.ViaCidr == "" {
|
||||
ob.Gateway = h.senderSettings.Via.AsAddress()
|
||||
if h.senderSettings.Via.AsAddress().Family().IsDomain() && h.senderSettings.Via.AsAddress().Domain() == "origin" {
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
origin, _, err := net.SplitHostPort(inbound.Conn.LocalAddr().String())
|
||||
if err == nil {
|
||||
ob.Gateway = net.ParseAddress(origin)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ob.Gateway = h.senderSettings.Via.AsAddress()
|
||||
}
|
||||
} else { //Get a random address.
|
||||
ob.Gateway = ParseRandomIPv6(h.senderSettings.Via.AsAddress(), h.senderSettings.ViaCidr)
|
||||
}
|
||||
|
@@ -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",
|
||||
|
@@ -78,3 +78,13 @@ func (c *OnlineMap) RemoveExpiredIPs(list map[string]time.Time) map[string]time.
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (c *OnlineMap) IpTimeMap() map[string]time.Time {
|
||||
list := c.ipList
|
||||
if time.Since(c.lastCleanup) > c.cleanupPeriod {
|
||||
list = c.RemoveExpiredIPs(list)
|
||||
c.lastCleanup = time.Now()
|
||||
}
|
||||
|
||||
return c.ipList
|
||||
}
|
||||
|
@@ -1,2 +1,15 @@
|
||||
// Package crypto provides common crypto libraries for Xray.
|
||||
package crypto // import "github.com/xtls/xray-core/common/crypto"
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func RandBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||
return from + bigInt.Int64()
|
||||
}
|
||||
|
@@ -120,7 +120,7 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu
|
||||
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
// deep-clone outbounds because it is going to be mutated concurrently
|
||||
// (Target and OriginalTarget)
|
||||
ctx = session.ContextCloneOutbounds(ctx)
|
||||
ctx = session.ContextCloneOutboundsAndContent(ctx)
|
||||
errors.LogInfo(ctx, "received request for ", meta.Target)
|
||||
{
|
||||
msg := &log.AccessMessage{
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -1,2 +1,14 @@
|
||||
// Package net is a drop-in replacement to Golang's net package, with some more functionalities.
|
||||
package net // import "github.com/xtls/xray-core/common/net"
|
||||
|
||||
import "time"
|
||||
|
||||
// defines the maximum time an idle TCP session can survive in the tunnel, so
|
||||
// it should be consistent across HTTP versions and with other transports.
|
||||
const ConnIdleTimeout = 300 * time.Second
|
||||
|
||||
// consistent with quic-go
|
||||
const QuicgoH3KeepAlivePeriod = 10 * time.Second
|
||||
|
||||
// consistent with chrome
|
||||
const ChromeH2KeepAlivePeriod = 45 * time.Second
|
||||
|
@@ -76,8 +76,9 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
ResolveUnixAddr = net.ResolveUnixAddr
|
||||
ResolveTCPAddr = net.ResolveTCPAddr
|
||||
ResolveUDPAddr = net.ResolveUDPAddr
|
||||
ResolveUnixAddr = net.ResolveUnixAddr
|
||||
)
|
||||
|
||||
type Resolver = net.Resolver
|
||||
|
@@ -63,7 +63,7 @@ func SniffHTTP(b []byte, c context.Context) (*SniffHeader, error) {
|
||||
ShouldSniffAttr := true
|
||||
// If content.Attributes have information, that means it comes from HTTP inbound PlainHTTP mode.
|
||||
// It will set attributes, so skip it.
|
||||
if content == nil || content.AttributeLen() != 0 {
|
||||
if content == nil || len(content.Attributes) != 0 {
|
||||
ShouldSniffAttr = false
|
||||
}
|
||||
if err := beginWithHTTPMethod(b); err != nil {
|
||||
|
3
common/protocol/tls/cert/.gitignore
vendored
3
common/protocol/tls/cert/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
*.pem
|
||||
*.crt
|
||||
*.key
|
@@ -78,9 +78,9 @@ func printJSON(certificate *Certificate) {
|
||||
func printFile(certificate *Certificate, name string) error {
|
||||
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")
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -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 {
|
||||
@@ -40,7 +42,7 @@ func ContextWithOutbounds(ctx context.Context, outbounds []*Outbound) context.Co
|
||||
return context.WithValue(ctx, outboundSessionKey, outbounds)
|
||||
}
|
||||
|
||||
func ContextCloneOutbounds(ctx context.Context) context.Context {
|
||||
func ContextCloneOutboundsAndContent(ctx context.Context) context.Context {
|
||||
outbounds := OutboundsFromContext(ctx)
|
||||
newOutbounds := make([]*Outbound, len(outbounds))
|
||||
for i, ob := range outbounds {
|
||||
@@ -53,7 +55,15 @@ func ContextCloneOutbounds(ctx context.Context) context.Context {
|
||||
newOutbounds[i] = &v
|
||||
}
|
||||
|
||||
return ContextWithOutbounds(ctx, newOutbounds)
|
||||
content := ContentFromContext(ctx)
|
||||
newContent := Content{}
|
||||
if content != nil {
|
||||
newContent = *content
|
||||
if content.Attributes != nil {
|
||||
panic("content.Attributes != nil")
|
||||
}
|
||||
}
|
||||
return ContextWithContent(ContextWithOutbounds(ctx, newOutbounds), &newContent)
|
||||
}
|
||||
|
||||
func OutboundsFromContext(ctx context.Context) []*Outbound {
|
||||
@@ -162,3 +172,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 ""
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ package session // import "github.com/xtls/xray-core/common/session"
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
c "github.com/xtls/xray-core/common/ctx"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -75,8 +74,8 @@ type Outbound struct {
|
||||
|
||||
// SniffingRequest controls the behavior of content sniffing.
|
||||
type SniffingRequest struct {
|
||||
ExcludeForDomain []string
|
||||
OverrideDestinationForProtocol []string
|
||||
ExcludeForDomain []string // read-only once set
|
||||
OverrideDestinationForProtocol []string // read-only once set
|
||||
Enabled bool
|
||||
MetadataOnly bool
|
||||
RouteOnly bool
|
||||
@@ -92,10 +91,6 @@ type Content struct {
|
||||
Attributes map[string]string
|
||||
|
||||
SkipDNSResolve bool
|
||||
|
||||
mu sync.Mutex
|
||||
|
||||
isLocked bool
|
||||
}
|
||||
|
||||
// Sockopt is the settings for socket connection.
|
||||
@@ -104,22 +99,8 @@ type Sockopt struct {
|
||||
Mark int32
|
||||
}
|
||||
|
||||
// Some how when using mux, there will be a same ctx between different requests
|
||||
// This will cause problem as it's designed for single request, like concurrent map writes
|
||||
// Add a Mutex as a temp solution
|
||||
|
||||
// SetAttribute attaches additional string attributes to content.
|
||||
func (c *Content) SetAttribute(name string, value string) {
|
||||
if c.isLocked {
|
||||
errors.LogError(context.Background(), "Multiple goroutines are tring to access one routing content, tring to write ", name, ":", value)
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.isLocked = true
|
||||
defer func() {
|
||||
c.isLocked = false
|
||||
c.mu.Unlock()
|
||||
}()
|
||||
|
||||
if c.Attributes == nil {
|
||||
c.Attributes = make(map[string]string)
|
||||
}
|
||||
@@ -128,24 +109,8 @@ func (c *Content) SetAttribute(name string, value string) {
|
||||
|
||||
// Attribute retrieves additional string attributes from content.
|
||||
func (c *Content) Attribute(name string) string {
|
||||
c.mu.Lock()
|
||||
c.isLocked = true
|
||||
defer func() {
|
||||
c.isLocked = false
|
||||
c.mu.Unlock()
|
||||
}()
|
||||
if c.Attributes == nil {
|
||||
return ""
|
||||
}
|
||||
return c.Attributes[name]
|
||||
}
|
||||
|
||||
func (c *Content) AttributeLen() int {
|
||||
c.mu.Lock()
|
||||
c.isLocked = true
|
||||
defer func() {
|
||||
c.isLocked = false
|
||||
c.mu.Unlock()
|
||||
}()
|
||||
return len(c.Attributes)
|
||||
}
|
||||
|
@@ -18,8 +18,8 @@ import (
|
||||
|
||||
var (
|
||||
Version_x byte = 25
|
||||
Version_y byte = 1
|
||||
Version_z byte = 30
|
||||
Version_y byte = 3
|
||||
Version_z byte = 6
|
||||
)
|
||||
|
||||
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
|
||||
|
@@ -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.
|
||||
|
26
go.mod
26
go.mod
@@ -1,18 +1,18 @@
|
||||
module github.com/xtls/xray-core
|
||||
|
||||
go 1.23
|
||||
go 1.24
|
||||
|
||||
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/google/go-cmp v0.7.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
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/quic-go/quic-go v0.50.0
|
||||
github.com/refraction-networking/utls v1.6.7
|
||||
github.com/sagernet/sing v0.5.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
@@ -22,16 +22,16 @@ require (
|
||||
github.com/vishvananda/netlink v1.3.0
|
||||
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/net v0.34.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.29.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/sync v0.12.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/grpc v1.70.0
|
||||
google.golang.org/protobuf v1.36.4
|
||||
google.golang.org/grpc v1.71.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
|
||||
lukechampine.com/blake3 v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -51,11 +51,11 @@ require (
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
70
go.sum
70
go.sum
@@ -2,8 +2,8 @@ github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJS
|
||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
||||
github.com/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=
|
||||
@@ -24,8 +24,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
|
||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@@ -54,8 +54,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
||||
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
@@ -79,24 +79,26 @@ github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZla
|
||||
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg=
|
||||
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/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.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
@@ -105,12 +107,12 @@ golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-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.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -119,14 +121,14 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/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/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -141,12 +143,12 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/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-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.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
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=
|
||||
@@ -160,5 +162,5 @@ gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 h1:P+U/06iIKPQ3DLcg+zBfSCia
|
||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
||||
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=
|
||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
||||
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -411,6 +411,7 @@ type TLSConfig struct {
|
||||
CurvePreferences *StringList `json:"curvePreferences"`
|
||||
MasterKeyLog string `json:"masterKeyLog"`
|
||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -432,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)
|
||||
}
|
||||
@@ -442,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
|
||||
|
||||
@@ -469,10 +477,11 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
|
||||
config.MasterKeyLog = c.MasterKeyLog
|
||||
config.ServerNameToVerify = c.ServerNameToVerify
|
||||
if config.ServerNameToVerify != "" && config.Fingerprint == "unsafe" {
|
||||
return nil, errors.New(`serverNameToVerify only works with uTLS for now`)
|
||||
|
||||
if c.ServerNameToVerify != "" {
|
||||
return nil, errors.PrintRemovedFeatureError(`"serverNameToVerify"`, `"verifyPeerCertInNames"`)
|
||||
}
|
||||
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
||||
|
||||
return config, nil
|
||||
}
|
||||
@@ -493,6 +502,7 @@ type REALITYConfig struct {
|
||||
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
ServerName string `json:"serverName"`
|
||||
Password string `json:"password"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
ShortId string `json:"shortId"`
|
||||
SpiderX string `json:"spiderX"`
|
||||
@@ -601,11 +611,14 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
||||
if len(c.ServerNames) != 0 {
|
||||
return nil, errors.New(`non-empty "serverNames", please use "serverName" instead`)
|
||||
}
|
||||
if c.Password != "" {
|
||||
c.PublicKey = c.Password
|
||||
}
|
||||
if c.PublicKey == "" {
|
||||
return nil, errors.New(`empty "publicKey"`)
|
||||
return nil, errors.New(`empty "password"`)
|
||||
}
|
||||
if config.PublicKey, err = base64.RawURLEncoding.DecodeString(c.PublicKey); err != nil || len(config.PublicKey) != 32 {
|
||||
return nil, errors.New(`invalid "publicKey": `, c.PublicKey)
|
||||
return nil, errors.New(`invalid "password": `, c.PublicKey)
|
||||
}
|
||||
if len(c.ShortIds) != 0 {
|
||||
return nil, errors.New(`non-empty "shortIds", please use "shortId" instead`)
|
||||
@@ -702,6 +715,7 @@ type SocketConfig struct {
|
||||
Interface string `json:"interface"`
|
||||
TcpMptcp bool `json:"tcpMptcp"`
|
||||
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
|
||||
AddressPortStrategy string `json:"addressPortStrategy"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -771,6 +785,26 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
||||
customSockopts = append(customSockopts, customSockopt)
|
||||
}
|
||||
|
||||
addressPortStrategy := internet.AddressPortStrategy_None
|
||||
switch strings.ToLower(c.AddressPortStrategy) {
|
||||
case "none", "":
|
||||
addressPortStrategy = internet.AddressPortStrategy_None
|
||||
case "srvportonly":
|
||||
addressPortStrategy = internet.AddressPortStrategy_SrvPortOnly
|
||||
case "srvaddressonly":
|
||||
addressPortStrategy = internet.AddressPortStrategy_SrvAddressOnly
|
||||
case "srvportandaddress":
|
||||
addressPortStrategy = internet.AddressPortStrategy_SrvPortAndAddress
|
||||
case "txtportonly":
|
||||
addressPortStrategy = internet.AddressPortStrategy_TxtPortOnly
|
||||
case "txtaddressonly":
|
||||
addressPortStrategy = internet.AddressPortStrategy_TxtAddressOnly
|
||||
case "txtportandaddress":
|
||||
addressPortStrategy = internet.AddressPortStrategy_TxtPortAndAddress
|
||||
default:
|
||||
return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy)
|
||||
}
|
||||
|
||||
return &internet.SocketConfig{
|
||||
Mark: c.Mark,
|
||||
Tfo: tfo,
|
||||
@@ -789,6 +823,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
||||
Interface: c.Interface,
|
||||
TcpMptcp: c.TcpMptcp,
|
||||
CustomSockopt: customSockopts,
|
||||
AddressPortStrategy: addressPortStrategy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@@ -292,7 +292,9 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
||||
senderSettings.ViaCidr = strings.Split(*c.SendThrough, "/")[1]
|
||||
} else {
|
||||
if address.Family().IsDomain() {
|
||||
return nil, errors.New("unable to send through: " + address.String())
|
||||
if address.Address.Domain() != "origin" {
|
||||
return nil, errors.New("unable to send through: " + address.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
senderSettings.Via = address.Build()
|
||||
|
@@ -89,12 +89,11 @@ func whichProtoc(suffix, targetedVersion string) (string, error) {
|
||||
|
||||
path, err := exec.LookPath(protoc)
|
||||
if err != nil {
|
||||
errStr := fmt.Sprintf(`
|
||||
return "", fmt.Errorf(`
|
||||
Command "%s" not found.
|
||||
Make sure that %s is in your system path or current path.
|
||||
Download %s v%s or later from https://github.com/protocolbuffers/protobuf/releases
|
||||
`, protoc, protoc, protoc, targetedVersion)
|
||||
return "", fmt.Errorf(errStr)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
@@ -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,
|
||||
}
|
||||
|
@@ -50,17 +50,17 @@ func executeTypedMessageToJson(cmd *base.Command, args []string) {
|
||||
|
||||
reader, err := confloader.LoadConfig(cmd.Flag.Arg(0))
|
||||
if err != nil {
|
||||
base.Fatalf(err.Error())
|
||||
base.Fatalf("failed to load config: %s", err)
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
base.Fatalf(err.Error())
|
||||
base.Fatalf("failed to read config: %s", err)
|
||||
}
|
||||
|
||||
tm := cserial.TypedMessage{}
|
||||
if err = json.Unmarshal(b, &tm); err != nil {
|
||||
base.Fatalf(err.Error())
|
||||
base.Fatalf("failed to unmarshal config: %s", err)
|
||||
}
|
||||
|
||||
if j, ok := creflect.MarshalToJson(&tm, injectTypeInfo); ok {
|
||||
|
@@ -53,12 +53,12 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
|
||||
}
|
||||
|
||||
if len(unnamedArgs) < 1 {
|
||||
base.Fatalf("empty config list")
|
||||
base.Fatalf("invalid config list length: %d", len(unnamedArgs))
|
||||
}
|
||||
|
||||
pbConfig, err := core.LoadConfig("auto", unnamedArgs)
|
||||
if err != nil {
|
||||
base.Fatalf(err.Error())
|
||||
base.Fatalf("failed to load config: %s", err)
|
||||
}
|
||||
|
||||
if optDump {
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,12 +4,12 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -414,7 +414,7 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
noise = n.Packet
|
||||
} else {
|
||||
//Random noise
|
||||
noise, err = GenerateRandomBytes(randBetween(int64(n.LengthMin),
|
||||
noise, err = GenerateRandomBytes(crypto.RandBetween(int64(n.LengthMin),
|
||||
int64(n.LengthMax)))
|
||||
}
|
||||
if err != nil {
|
||||
@@ -423,7 +423,7 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)})
|
||||
|
||||
if n.DelayMin != 0 || n.DelayMax != 0 {
|
||||
time.Sleep(time.Duration(randBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond)
|
||||
time.Sleep(time.Duration(crypto.RandBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
||||
buf := make([]byte, 1024)
|
||||
var hello []byte
|
||||
for from := 0; ; {
|
||||
to := from + int(randBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
||||
to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
||||
if to > len(data) {
|
||||
to = len(data)
|
||||
}
|
||||
@@ -466,7 +466,7 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
||||
hello = append(hello, buf[:5+l]...)
|
||||
} else {
|
||||
_, err := f.writer.Write(buf[:5+l])
|
||||
time.Sleep(time.Duration(randBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
||||
time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -493,13 +493,13 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
||||
return f.writer.Write(b)
|
||||
}
|
||||
for from := 0; ; {
|
||||
to := from + int(randBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
||||
to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
|
||||
if to > len(b) {
|
||||
to = len(b)
|
||||
}
|
||||
n, err := f.writer.Write(b[from:to])
|
||||
from += n
|
||||
time.Sleep(time.Duration(randBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
||||
time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
|
||||
if err != nil {
|
||||
return from, err
|
||||
}
|
||||
@@ -509,14 +509,6 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// stolen from github.com/xtls/xray-core/transport/internet/reality
|
||||
func randBetween(left int64, right int64) int64 {
|
||||
if left == right {
|
||||
return left
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left))
|
||||
return left + bigInt.Int64()
|
||||
}
|
||||
func GenerateRandomBytes(n int64) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
|
@@ -151,6 +151,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
||||
}
|
||||
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)
|
||||
|
200
proxy/proxy.go
200
proxy/proxy.go
@@ -107,41 +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
|
||||
DownlinkReaderDirectCopy bool
|
||||
UplinkReaderDirectCopy bool
|
||||
RemainingCommand int32
|
||||
RemainingContent int32
|
||||
RemainingPadding int32
|
||||
CurrentCommand int
|
||||
|
||||
// write link state
|
||||
IsPadding bool
|
||||
DownlinkWriterDirectCopy bool
|
||||
UplinkWriterDirectCopy 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,
|
||||
DownlinkReaderDirectCopy: false,
|
||||
UplinkReaderDirectCopy: false,
|
||||
RemainingCommand: -1,
|
||||
RemainingContent: -1,
|
||||
RemainingPadding: -1,
|
||||
CurrentCommand: 0,
|
||||
IsPadding: true,
|
||||
DownlinkWriterDirectCopy: false,
|
||||
UplinkWriterDirectCopy: 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,28 +190,43 @@ func NewVisionReader(reader buf.Reader, state *TrafficState, isUplink bool, cont
|
||||
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
|
||||
if w.isUplink {
|
||||
w.trafficState.UplinkReaderDirectCopy = true
|
||||
} else {
|
||||
w.trafficState.DownlinkReaderDirectCopy = 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 {
|
||||
@@ -223,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)
|
||||
@@ -233,11 +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 {
|
||||
if w.isUplink {
|
||||
w.trafficState.UplinkWriterDirectCopy = true
|
||||
} else {
|
||||
w.trafficState.DownlinkWriterDirectCopy = true
|
||||
}
|
||||
*switchToDirectCopy = true
|
||||
}
|
||||
var command byte = CommandPaddingContinue
|
||||
if i == len(mb)-1 {
|
||||
@@ -247,16 +291,16 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
}
|
||||
}
|
||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx)
|
||||
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
|
||||
@@ -343,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()
|
||||
}
|
||||
@@ -383,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())
|
||||
}
|
||||
@@ -465,7 +524,7 @@ func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx conte
|
||||
}
|
||||
}
|
||||
|
||||
// UnwrapRawConn support unwrap stats, tls, utls, reality and proxyproto conn and get raw tcp conn from it
|
||||
// UnwrapRawConn support unwrap stats, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it
|
||||
func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
||||
var readCounter, writerCounter stats.Counter
|
||||
if conn != nil {
|
||||
@@ -488,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.UnixConnWrapper); ok {
|
||||
conn = uc.UnixConn
|
||||
}
|
||||
}
|
||||
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))
|
||||
|
@@ -199,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)
|
||||
@@ -256,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
|
||||
|
||||
|
@@ -175,16 +175,16 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
|
||||
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
||||
err := func() error {
|
||||
for {
|
||||
if isUplink && trafficState.UplinkReaderDirectCopy || !isUplink && trafficState.DownlinkReaderDirectCopy {
|
||||
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 isUplink && trafficState.UplinkReaderDirectCopy || !isUplink && trafficState.DownlinkReaderDirectCopy {
|
||||
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||
// XTLS Vision processes struct TLS Conn's input and rawInput
|
||||
if inputBuffer, err := buf.ReadFrom(input); err == nil {
|
||||
if !inputBuffer.IsEmpty() {
|
||||
@@ -227,12 +227,12 @@ func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdate
|
||||
var ct stats.Counter
|
||||
for {
|
||||
buffer, err := reader.ReadMultiBuffer()
|
||||
if isUplink && trafficState.UplinkWriterDirectCopy || !isUplink && trafficState.DownlinkWriterDirectCopy {
|
||||
if isUplink && trafficState.Outbound.UplinkWriterDirectCopy || !isUplink && trafficState.Inbound.DownlinkWriterDirectCopy {
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
if inbound.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
|
||||
}
|
||||
}
|
||||
@@ -240,9 +240,9 @@ func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdate
|
||||
writer = buf.NewWriter(rawConn)
|
||||
ct = writerCounter
|
||||
if isUplink {
|
||||
trafficState.UplinkWriterDirectCopy = false
|
||||
trafficState.Outbound.UplinkWriterDirectCopy = false
|
||||
} else {
|
||||
trafficState.DownlinkWriterDirectCopy = false
|
||||
trafficState.Inbound.DownlinkWriterDirectCopy = false
|
||||
}
|
||||
}
|
||||
if !buffer.IsEmpty() {
|
||||
|
@@ -95,6 +95,67 @@ func (DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type AddressPortStrategy int32
|
||||
|
||||
const (
|
||||
AddressPortStrategy_None AddressPortStrategy = 0
|
||||
AddressPortStrategy_SrvPortOnly AddressPortStrategy = 1
|
||||
AddressPortStrategy_SrvAddressOnly AddressPortStrategy = 2
|
||||
AddressPortStrategy_SrvPortAndAddress AddressPortStrategy = 3
|
||||
AddressPortStrategy_TxtPortOnly AddressPortStrategy = 4
|
||||
AddressPortStrategy_TxtAddressOnly AddressPortStrategy = 5
|
||||
AddressPortStrategy_TxtPortAndAddress AddressPortStrategy = 6
|
||||
)
|
||||
|
||||
// Enum value maps for AddressPortStrategy.
|
||||
var (
|
||||
AddressPortStrategy_name = map[int32]string{
|
||||
0: "None",
|
||||
1: "SrvPortOnly",
|
||||
2: "SrvAddressOnly",
|
||||
3: "SrvPortAndAddress",
|
||||
4: "TxtPortOnly",
|
||||
5: "TxtAddressOnly",
|
||||
6: "TxtPortAndAddress",
|
||||
}
|
||||
AddressPortStrategy_value = map[string]int32{
|
||||
"None": 0,
|
||||
"SrvPortOnly": 1,
|
||||
"SrvAddressOnly": 2,
|
||||
"SrvPortAndAddress": 3,
|
||||
"TxtPortOnly": 4,
|
||||
"TxtAddressOnly": 5,
|
||||
"TxtPortAndAddress": 6,
|
||||
}
|
||||
)
|
||||
|
||||
func (x AddressPortStrategy) Enum() *AddressPortStrategy {
|
||||
p := new(AddressPortStrategy)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x AddressPortStrategy) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (AddressPortStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_transport_internet_config_proto_enumTypes[1].Descriptor()
|
||||
}
|
||||
|
||||
func (AddressPortStrategy) Type() protoreflect.EnumType {
|
||||
return &file_transport_internet_config_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x AddressPortStrategy) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddressPortStrategy.Descriptor instead.
|
||||
func (AddressPortStrategy) EnumDescriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
type SocketConfig_TProxyMode int32
|
||||
|
||||
const (
|
||||
@@ -131,11 +192,11 @@ func (x SocketConfig_TProxyMode) String() string {
|
||||
}
|
||||
|
||||
func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_transport_internet_config_proto_enumTypes[1].Descriptor()
|
||||
return file_transport_internet_config_proto_enumTypes[2].Descriptor()
|
||||
}
|
||||
|
||||
func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
|
||||
return &file_transport_internet_config_proto_enumTypes[1]
|
||||
return &file_transport_internet_config_proto_enumTypes[2]
|
||||
}
|
||||
|
||||
func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
|
||||
@@ -434,23 +495,24 @@ type SocketConfig struct {
|
||||
Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=xray.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"`
|
||||
// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
|
||||
// option. This option is for UDP only.
|
||||
ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
|
||||
BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
|
||||
BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
|
||||
AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
|
||||
DomainStrategy DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
|
||||
DialerProxy string `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"`
|
||||
TcpKeepAliveInterval int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
|
||||
TcpKeepAliveIdle int32 `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
|
||||
TcpCongestion string `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"`
|
||||
Interface string `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"`
|
||||
V6Only bool `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"`
|
||||
TcpWindowClamp int32 `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"`
|
||||
TcpUserTimeout int32 `protobuf:"varint,16,opt,name=tcp_user_timeout,json=tcpUserTimeout,proto3" json:"tcp_user_timeout,omitempty"`
|
||||
TcpMaxSeg int32 `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"`
|
||||
Penetrate bool `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
|
||||
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
||||
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
|
||||
ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
|
||||
BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
|
||||
BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
|
||||
AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
|
||||
DomainStrategy DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
|
||||
DialerProxy string `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"`
|
||||
TcpKeepAliveInterval int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
|
||||
TcpKeepAliveIdle int32 `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
|
||||
TcpCongestion string `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"`
|
||||
Interface string `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"`
|
||||
V6Only bool `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"`
|
||||
TcpWindowClamp int32 `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"`
|
||||
TcpUserTimeout int32 `protobuf:"varint,16,opt,name=tcp_user_timeout,json=tcpUserTimeout,proto3" json:"tcp_user_timeout,omitempty"`
|
||||
TcpMaxSeg int32 `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"`
|
||||
Penetrate bool `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
|
||||
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
||||
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
|
||||
AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SocketConfig) Reset() {
|
||||
@@ -623,6 +685,13 @@ func (x *SocketConfig) GetCustomSockopt() []*CustomSockopt {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SocketConfig) GetAddressPortStrategy() AddressPortStrategy {
|
||||
if x != nil {
|
||||
return x.AddressPortStrategy
|
||||
}
|
||||
return AddressPortStrategy_None
|
||||
}
|
||||
|
||||
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_config_proto_rawDesc = []byte{
|
||||
@@ -678,7 +747,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
|
||||
0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
|
||||
0x65, 0x22, 0x9b, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x65, 0x22, 0xfd, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
|
||||
0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f,
|
||||
@@ -732,28 +801,44 @@ var file_transport_internet_config_proto_rawDesc = []byte{
|
||||
0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
||||
0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x52,
|
||||
0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x22, 0x2f,
|
||||
0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03,
|
||||
0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10,
|
||||
0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a,
|
||||
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, 0x67, 0x0a, 0x1b, 0x63,
|
||||
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x12, 0x60,
|
||||
0x0a, 0x15, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73,
|
||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50,
|
||||
0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||
0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07,
|
||||
0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10,
|
||||
0x02, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12,
|
||||
0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55,
|
||||
0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f,
|
||||
0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
|
||||
0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10,
|
||||
0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12,
|
||||
0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d,
|
||||
0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a,
|
||||
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a,
|
||||
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01,
|
||||
0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72,
|
||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12,
|
||||
0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01,
|
||||
0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e,
|
||||
0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41,
|
||||
0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54,
|
||||
0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e,
|
||||
0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05,
|
||||
0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -768,33 +853,35 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
|
||||
return file_transport_internet_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_transport_internet_config_proto_goTypes = []any{
|
||||
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
|
||||
(SocketConfig_TProxyMode)(0), // 1: xray.transport.internet.SocketConfig.TProxyMode
|
||||
(*TransportConfig)(nil), // 2: xray.transport.internet.TransportConfig
|
||||
(*StreamConfig)(nil), // 3: xray.transport.internet.StreamConfig
|
||||
(*ProxyConfig)(nil), // 4: xray.transport.internet.ProxyConfig
|
||||
(*CustomSockopt)(nil), // 5: xray.transport.internet.CustomSockopt
|
||||
(*SocketConfig)(nil), // 6: xray.transport.internet.SocketConfig
|
||||
(*serial.TypedMessage)(nil), // 7: xray.common.serial.TypedMessage
|
||||
(*net.IPOrDomain)(nil), // 8: xray.common.net.IPOrDomain
|
||||
(AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy
|
||||
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
||||
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
|
||||
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
||||
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
|
||||
(*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt
|
||||
(*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig
|
||||
(*serial.TypedMessage)(nil), // 8: xray.common.serial.TypedMessage
|
||||
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain
|
||||
}
|
||||
var file_transport_internet_config_proto_depIdxs = []int32{
|
||||
7, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||
8, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
2, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||
7, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
6, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
1, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
8, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||
8, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
7, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
2, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 6: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
5, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
8, // [8:8] is the sub-list for method output_type
|
||||
8, // [8:8] is the sub-list for method input_type
|
||||
8, // [8:8] is the sub-list for extension type_name
|
||||
8, // [8:8] is the sub-list for extension extendee
|
||||
0, // [0:8] is the sub-list for field type_name
|
||||
6, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 8: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
9, // [9:9] is the sub-list for method output_type
|
||||
9, // [9:9] is the sub-list for method input_type
|
||||
9, // [9:9] is the sub-list for extension type_name
|
||||
9, // [9:9] is the sub-list for extension extendee
|
||||
0, // [0:9] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_config_proto_init() }
|
||||
@@ -807,7 +894,7 @@ func file_transport_internet_config_proto_init() {
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_transport_internet_config_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumEnums: 3,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
|
@@ -23,6 +23,16 @@ enum DomainStrategy {
|
||||
FORCE_IP64 = 10;
|
||||
}
|
||||
|
||||
enum AddressPortStrategy {
|
||||
None = 0;
|
||||
SrvPortOnly = 1;
|
||||
SrvAddressOnly = 2;
|
||||
SrvPortAndAddress = 3;
|
||||
TxtPortOnly = 4;
|
||||
TxtAddressOnly = 5;
|
||||
TxtPortAndAddress = 6;
|
||||
}
|
||||
|
||||
message TransportConfig {
|
||||
// Transport protocol name.
|
||||
string protocol_name = 3;
|
||||
@@ -116,4 +126,6 @@ message SocketConfig {
|
||||
bool tcp_mptcp = 19;
|
||||
|
||||
repeated CustomSockopt customSockopt = 20;
|
||||
|
||||
AddressPortStrategy address_port_strategy = 21;
|
||||
}
|
||||
|
@@ -2,6 +2,9 @@ package internet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
gonet "net"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
@@ -140,6 +143,93 @@ func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkAddressPortStrategy(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (*net.Destination, error) {
|
||||
if sockopt.AddressPortStrategy == AddressPortStrategy_None {
|
||||
return nil, nil
|
||||
}
|
||||
newDest := dest
|
||||
var OverridePort, OverrideAddress bool
|
||||
var OverrideBy string
|
||||
switch sockopt.AddressPortStrategy {
|
||||
case AddressPortStrategy_SrvPortOnly:
|
||||
OverridePort = true
|
||||
OverrideAddress = false
|
||||
OverrideBy = "srv"
|
||||
case AddressPortStrategy_SrvAddressOnly:
|
||||
OverridePort = false
|
||||
OverrideAddress = true
|
||||
OverrideBy = "srv"
|
||||
case AddressPortStrategy_SrvPortAndAddress:
|
||||
OverridePort = true
|
||||
OverrideAddress = true
|
||||
OverrideBy = "srv"
|
||||
case AddressPortStrategy_TxtPortOnly:
|
||||
OverridePort = true
|
||||
OverrideAddress = false
|
||||
OverrideBy = "txt"
|
||||
case AddressPortStrategy_TxtAddressOnly:
|
||||
OverridePort = false
|
||||
OverrideAddress = true
|
||||
OverrideBy = "txt"
|
||||
case AddressPortStrategy_TxtPortAndAddress:
|
||||
OverridePort = true
|
||||
OverrideAddress = true
|
||||
OverrideBy = "txt"
|
||||
default:
|
||||
return nil, errors.New("unknown AddressPortStrategy")
|
||||
}
|
||||
|
||||
if !dest.Address.Family().IsDomain() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if OverrideBy == "srv" {
|
||||
errors.LogDebug(ctx, "query SRV record for "+dest.Address.String())
|
||||
parts := strings.SplitN(dest.Address.String(), ".", 3)
|
||||
if len(parts) != 3 {
|
||||
return nil, errors.New("invalid address format", dest.Address.String())
|
||||
}
|
||||
_, srvRecords, err := gonet.DefaultResolver.LookupSRV(context.Background(), parts[0][1:], parts[1][1:], parts[2])
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to lookup SRV record").Base(err)
|
||||
}
|
||||
errors.LogDebug(ctx, "SRV record: "+fmt.Sprintf("addr=%s, port=%d, priority=%d, weight=%d", srvRecords[0].Target, srvRecords[0].Port, srvRecords[0].Priority, srvRecords[0].Weight))
|
||||
if OverridePort {
|
||||
newDest.Port = net.Port(srvRecords[0].Port)
|
||||
}
|
||||
if OverrideAddress {
|
||||
newDest.Address = net.ParseAddress(srvRecords[0].Target)
|
||||
}
|
||||
return &newDest, nil
|
||||
}
|
||||
if OverrideBy == "txt" {
|
||||
errors.LogDebug(ctx, "query TXT record for "+dest.Address.String())
|
||||
txtRecords, err := gonet.DefaultResolver.LookupTXT(ctx, dest.Address.String())
|
||||
if err != nil {
|
||||
errors.LogError(ctx, "failed to lookup SRV record: "+err.Error())
|
||||
return nil, errors.New("failed to lookup SRV record").Base(err)
|
||||
}
|
||||
for _, txtRecord := range txtRecords {
|
||||
errors.LogDebug(ctx, "TXT record: "+txtRecord)
|
||||
addr_s, port_s, _ := net.SplitHostPort(string(txtRecord))
|
||||
addr := net.ParseAddress(addr_s)
|
||||
port, err := net.PortFromString(port_s)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if OverridePort {
|
||||
newDest.Port = port
|
||||
}
|
||||
if OverrideAddress {
|
||||
newDest.Address = addr
|
||||
}
|
||||
return &newDest, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DialSystem calls system dialer to create a network connection.
|
||||
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
||||
var src net.Address
|
||||
@@ -152,6 +242,11 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig
|
||||
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
|
||||
}
|
||||
|
||||
if newDest, err := checkAddressPortStrategy(ctx, dest, sockopt); err == nil && newDest != nil {
|
||||
errors.LogInfo(ctx, "replace destination with "+newDest.String())
|
||||
dest = *newDest
|
||||
}
|
||||
|
||||
if canLookupIP(ctx, dest, sockopt) {
|
||||
ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
|
||||
if err == nil && len(ips) > 0 {
|
||||
|
@@ -8,7 +8,6 @@ import (
|
||||
"crypto/ecdh"
|
||||
"crypto/ed25519"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
gotls "crypto/tls"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@@ -27,6 +25,7 @@ import (
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"github.com/xtls/reality"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/core"
|
||||
@@ -180,12 +179,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)
|
||||
@@ -213,13 +212,13 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
||||
}
|
||||
times := 1
|
||||
if !first {
|
||||
times = int(randBetween(config.SpiderY[4], config.SpiderY[5]))
|
||||
times = int(crypto.RandBetween(config.SpiderY[4], config.SpiderY[5]))
|
||||
}
|
||||
for j := 0; j < times; j++ {
|
||||
if !first && j == 0 {
|
||||
req.Header.Set("Referer", firstURL)
|
||||
}
|
||||
req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(randBetween(config.SpiderY[0], config.SpiderY[1])))})
|
||||
req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(crypto.RandBetween(config.SpiderY[0], config.SpiderY[1])))})
|
||||
if resp, err = client.Do(req); err != nil {
|
||||
break
|
||||
}
|
||||
@@ -232,7 +231,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
||||
for _, m := range href.FindAllSubmatch(body, -1) {
|
||||
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)
|
||||
@@ -243,18 +242,18 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
||||
}
|
||||
maps.Unlock()
|
||||
if !first {
|
||||
time.Sleep(time.Duration(randBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval
|
||||
time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval
|
||||
}
|
||||
}
|
||||
}
|
||||
get(true)
|
||||
concurrency := int(randBetween(config.SpiderY[2], config.SpiderY[3]))
|
||||
concurrency := int(crypto.RandBetween(config.SpiderY[2], config.SpiderY[3]))
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go get(false)
|
||||
}
|
||||
// Do not close the connection
|
||||
}()
|
||||
time.Sleep(time.Duration(randBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return
|
||||
time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return
|
||||
return nil, errors.New("REALITY: processed invalid connection").AtWarning()
|
||||
}
|
||||
return uConn, nil
|
||||
@@ -267,11 +266,11 @@ 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 {
|
||||
stopAt := int(randBetween(0, int64(len(paths)-1)))
|
||||
func getPathLocked(paths map[string]struct{}) string {
|
||||
stopAt := int(crypto.RandBetween(0, int64(len(paths)-1)))
|
||||
i := 0
|
||||
for s := range paths {
|
||||
if i == stopAt {
|
||||
@@ -281,11 +280,3 @@ func getPathLocked(paths map[string]bool) string {
|
||||
}
|
||||
return "/"
|
||||
}
|
||||
|
||||
func randBetween(left int64, right int64) int64 {
|
||||
if left == right {
|
||||
return left
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left))
|
||||
return left + bigInt.Int64()
|
||||
}
|
||||
|
@@ -1,13 +1,12 @@
|
||||
package splithttp
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
@@ -34,13 +33,12 @@ func (c *Config) GetNormalizedQuery() string {
|
||||
query = pathAndQuery[1]
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
query += "&"
|
||||
}
|
||||
|
||||
// query += "x_version=" + core.Version()
|
||||
|
||||
query += "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().From))
|
||||
/*
|
||||
if query != "" {
|
||||
query += "&"
|
||||
}
|
||||
query += "x_version=" + core.Version()
|
||||
*/
|
||||
|
||||
return query
|
||||
}
|
||||
@@ -185,9 +183,5 @@ func init() {
|
||||
}
|
||||
|
||||
func (c RangeConfig) rand() int32 {
|
||||
if c.From == c.To {
|
||||
return c.From
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(c.To-c.From)))
|
||||
return c.From + int32(bigInt.Int64())
|
||||
return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
|
||||
}
|
||||
|
@@ -30,16 +30,6 @@ import (
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// defines the maximum time an idle TCP session can survive in the tunnel, so
|
||||
// it should be consistent across HTTP versions and with other transports.
|
||||
const connIdleTimeout = 300 * time.Second
|
||||
|
||||
// consistent with quic-go
|
||||
const quicgoH3KeepAlivePeriod = 10 * time.Second
|
||||
|
||||
// consistent with chrome
|
||||
const chromeH2KeepAlivePeriod = 45 * time.Second
|
||||
|
||||
type dialerConf struct {
|
||||
net.Destination
|
||||
*internet.MemoryStreamConfig
|
||||
@@ -154,13 +144,13 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
|
||||
if httpVersion == "3" {
|
||||
if keepAlivePeriod == 0 {
|
||||
keepAlivePeriod = quicgoH3KeepAlivePeriod
|
||||
keepAlivePeriod = net.QuicgoH3KeepAlivePeriod
|
||||
}
|
||||
if keepAlivePeriod < 0 {
|
||||
keepAlivePeriod = 0
|
||||
}
|
||||
quicConfig := &quic.Config{
|
||||
MaxIdleTimeout: connIdleTimeout,
|
||||
MaxIdleTimeout: net.ConnIdleTimeout,
|
||||
|
||||
// these two are defaults of quic-go/http3. the default of quic-go (no
|
||||
// http3) is different, so it is hardcoded here for clarity.
|
||||
@@ -168,7 +158,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
MaxIncomingStreams: -1,
|
||||
KeepAlivePeriod: keepAlivePeriod,
|
||||
}
|
||||
transport = &http3.RoundTripper{
|
||||
transport = &http3.Transport{
|
||||
QUICConfig: quicConfig,
|
||||
TLSClientConfig: gotlsConfig,
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||
@@ -198,7 +188,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
udpConn = &internet.FakePacketConn{c}
|
||||
udpConn = &internet.FakePacketConn{Conn: c}
|
||||
udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -210,7 +200,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
}
|
||||
} else if httpVersion == "2" {
|
||||
if keepAlivePeriod == 0 {
|
||||
keepAlivePeriod = chromeH2KeepAlivePeriod
|
||||
keepAlivePeriod = net.ChromeH2KeepAlivePeriod
|
||||
}
|
||||
if keepAlivePeriod < 0 {
|
||||
keepAlivePeriod = 0
|
||||
@@ -219,7 +209,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
|
||||
return dialContext(ctxInner)
|
||||
},
|
||||
IdleConnTimeout: connIdleTimeout,
|
||||
IdleConnTimeout: net.ConnIdleTimeout,
|
||||
ReadIdleTimeout: keepAlivePeriod,
|
||||
}
|
||||
} else {
|
||||
@@ -230,7 +220,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
transport = &http.Transport{
|
||||
DialTLSContext: httpDialContext,
|
||||
DialContext: httpDialContext,
|
||||
IdleConnTimeout: connIdleTimeout,
|
||||
IdleConnTimeout: net.ConnIdleTimeout,
|
||||
// chunked transfer download with KeepAlives is buggy with
|
||||
// http.Client and our custom dial context.
|
||||
DisableKeepAlives: true,
|
||||
|
@@ -3,9 +3,8 @@ package splithttp
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
gotls "crypto/tls"
|
||||
"io"
|
||||
gonet "net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -24,9 +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"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
type requestHandler struct {
|
||||
@@ -36,7 +33,7 @@ type requestHandler struct {
|
||||
ln *Listener
|
||||
sessionMu *sync.Mutex
|
||||
sessions sync.Map
|
||||
localAddr gonet.TCPAddr
|
||||
localAddr net.Addr
|
||||
}
|
||||
|
||||
type httpSession struct {
|
||||
@@ -48,21 +45,6 @@ type httpSession struct {
|
||||
isFullyConnected *done.Instance
|
||||
}
|
||||
|
||||
func (h *requestHandler) maybeReapSession(isFullyConnected *done.Instance, sessionId string) {
|
||||
shouldReap := done.New()
|
||||
go func() {
|
||||
time.Sleep(30 * time.Second)
|
||||
shouldReap.Close()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-isFullyConnected.Wait():
|
||||
return
|
||||
case <-shouldReap.Wait():
|
||||
h.sessions.Delete(sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
||||
// fast path
|
||||
currentSessionAny, ok := h.sessions.Load(sessionId)
|
||||
@@ -85,7 +67,21 @@ func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
||||
}
|
||||
|
||||
h.sessions.Store(sessionId, s)
|
||||
go h.maybeReapSession(s.isFullyConnected, sessionId)
|
||||
|
||||
shouldReap := done.New()
|
||||
go func() {
|
||||
time.Sleep(30 * time.Second)
|
||||
shouldReap.Close()
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
case <-shouldReap.Wait():
|
||||
h.sessions.Delete(sessionId)
|
||||
s.uploadQueue.Close()
|
||||
case <-s.isFullyConnected.Wait():
|
||||
}
|
||||
}()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -144,14 +140,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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +168,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]
|
||||
@@ -173,8 +180,13 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
httpSC := &httpServerConn{
|
||||
Instance: done.New(),
|
||||
Reader: request.Body,
|
||||
ResponseWriter: writer,
|
||||
}
|
||||
err = currentSession.uploadQueue.Push(Packet{
|
||||
Reader: request.Body,
|
||||
Reader: httpSC,
|
||||
})
|
||||
if err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to upload (PushReader)")
|
||||
@@ -186,21 +198,21 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
scStreamUpServerSecs := h.config.GetNormalizedScStreamUpServerSecs()
|
||||
if referrer != "" && scStreamUpServerSecs.To > 0 {
|
||||
go func() {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
for {
|
||||
_, err := writer.Write(bytes.Repeat([]byte{'X'}, int(h.config.GetNormalizedXPaddingBytes().rand())))
|
||||
_, err := httpSC.Write(bytes.Repeat([]byte{'X'}, int(h.config.GetNormalizedXPaddingBytes().rand())))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
writer.(http.Flusher).Flush()
|
||||
time.Sleep(time.Duration(scStreamUpServerSecs.rand()) * time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
<-request.Context().Done()
|
||||
select {
|
||||
case <-request.Context().Done():
|
||||
case <-httpSC.Wait():
|
||||
}
|
||||
}
|
||||
httpSC.Close()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -243,12 +255,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
} else if request.Method == "GET" || sessionId == "" {
|
||||
responseFlusher, ok := writer.(http.Flusher)
|
||||
if !ok {
|
||||
panic("expected http.ResponseWriter to be an http.Flusher")
|
||||
}
|
||||
|
||||
} else if request.Method == "GET" || sessionId == "" { // stream-down, stream-one
|
||||
if sessionId != "" {
|
||||
// after GET is done, the connection is finished. disable automatic
|
||||
// session reaping, and handle it in defer
|
||||
@@ -269,21 +276,20 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
writer.(http.Flusher).Flush()
|
||||
|
||||
responseFlusher.Flush()
|
||||
|
||||
downloadDone := done.New()
|
||||
|
||||
conn := splitConn{
|
||||
writer: &httpResponseBodyWriter{
|
||||
responseWriter: writer,
|
||||
downloadDone: downloadDone,
|
||||
responseFlusher: responseFlusher,
|
||||
},
|
||||
reader: request.Body,
|
||||
remoteAddr: remoteAddr,
|
||||
httpSC := &httpServerConn{
|
||||
Instance: done.New(),
|
||||
Reader: request.Body,
|
||||
ResponseWriter: writer,
|
||||
}
|
||||
if sessionId != "" {
|
||||
conn := splitConn{
|
||||
writer: httpSC,
|
||||
reader: httpSC,
|
||||
remoteAddr: remoteAddr,
|
||||
localAddr: h.localAddr,
|
||||
}
|
||||
if sessionId != "" { // if not stream-one
|
||||
conn.reader = currentSession.uploadQueue
|
||||
}
|
||||
|
||||
@@ -292,7 +298,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
// "A ResponseWriter may not be used after [Handler.ServeHTTP] has returned."
|
||||
select {
|
||||
case <-request.Context().Done():
|
||||
case <-downloadDone.Wait():
|
||||
case <-httpSC.Wait():
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
@@ -302,31 +308,30 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
}
|
||||
|
||||
type httpResponseBodyWriter struct {
|
||||
type httpServerConn struct {
|
||||
sync.Mutex
|
||||
responseWriter http.ResponseWriter
|
||||
responseFlusher http.Flusher
|
||||
downloadDone *done.Instance
|
||||
*done.Instance
|
||||
io.Reader // no need to Close request.Body
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
func (c *httpResponseBodyWriter) Write(b []byte) (int, error) {
|
||||
func (c *httpServerConn) Write(b []byte) (int, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.downloadDone.Done() {
|
||||
if c.Done() {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
n, err := c.responseWriter.Write(b)
|
||||
n, err := c.ResponseWriter.Write(b)
|
||||
if err == nil {
|
||||
c.responseFlusher.Flush()
|
||||
c.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *httpResponseBodyWriter) Close() error {
|
||||
func (c *httpServerConn) Close() error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.downloadDone.Close()
|
||||
return nil
|
||||
return c.Instance.Close()
|
||||
}
|
||||
|
||||
type Listener struct {
|
||||
@@ -340,34 +345,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)
|
||||
@@ -383,27 +384,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)
|
||||
@@ -414,29 +412,31 @@ 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())
|
||||
}
|
||||
|
||||
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
||||
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
||||
l.listener = listener
|
||||
handler.localAddr = l.listener.Addr()
|
||||
|
||||
// server can handle both plaintext HTTP/1.1 and h2c
|
||||
protocols := new(http.Protocols)
|
||||
protocols.SetHTTP1(true)
|
||||
protocols.SetUnencryptedHTTP2(true)
|
||||
l.server = http.Server{
|
||||
Handler: h2cHandler,
|
||||
Handler: handler,
|
||||
ReadHeaderTimeout: time.Second * 4,
|
||||
MaxHeaderBytes: 8192,
|
||||
Protocols: protocols,
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -466,13 +466,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))
|
||||
}
|
||||
|
@@ -3,10 +3,8 @@ package splithttp_test
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
gotls "crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
gonet "net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"testing"
|
||||
@@ -23,12 +21,11 @@ import (
|
||||
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
func Test_listenSHAndDial(t *testing.T) {
|
||||
func Test_ListenXHAndDial(t *testing.T) {
|
||||
listenPort := tcp.PickPort()
|
||||
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 +82,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 +122,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 +142,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 +177,7 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listenSHAndDial_H2C(t *testing.T) {
|
||||
func Test_ListenXHAndDial_H2C(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
return
|
||||
}
|
||||
@@ -193,7 +190,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()
|
||||
}()
|
||||
@@ -201,17 +198,11 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer listen.Close()
|
||||
|
||||
protocols := new(http.Protocols)
|
||||
protocols.SetUnencryptedHTTP2(true)
|
||||
client := http.Client{
|
||||
Transport: &http2.Transport{
|
||||
// So http2.Transport doesn't complain the URL scheme isn't 'https'
|
||||
AllowHTTP: true,
|
||||
// even with AllowHTTP, http2.Transport will attempt to establish
|
||||
// the connection using DialTLSContext. Disable TLS with custom
|
||||
// dial context.
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (gonet.Conn, error) {
|
||||
var d gonet.Dialer
|
||||
return d.DialContext(ctx, network, addr)
|
||||
},
|
||||
Transport: &http.Transport{
|
||||
Protocols: protocols,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -227,7 +218,7 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listenSHAndDial_QUIC(t *testing.T) {
|
||||
func Test_ListenXHAndDial_QUIC(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
return
|
||||
}
|
||||
@@ -250,7 +241,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 +300,11 @@ func Test_listenSHAndDial_QUIC(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listenSHAndDial_Unix(t *testing.T) {
|
||||
func Test_ListenXHAndDial_Unix(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
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 +364,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 +422,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
|
||||
|
@@ -20,6 +20,7 @@ type Packet struct {
|
||||
|
||||
type uploadQueue struct {
|
||||
reader io.ReadCloser
|
||||
nomore bool
|
||||
pushedPackets chan Packet
|
||||
writeCloseMutex sync.Mutex
|
||||
heap uploadHeap
|
||||
@@ -42,19 +43,15 @@ func (h *uploadQueue) Push(p Packet) error {
|
||||
h.writeCloseMutex.Lock()
|
||||
defer h.writeCloseMutex.Unlock()
|
||||
|
||||
runtime.Gosched()
|
||||
if h.reader != nil && p.Reader != nil {
|
||||
p.Reader.Close()
|
||||
return errors.New("h.reader already exists")
|
||||
}
|
||||
|
||||
if h.closed {
|
||||
if p.Reader != nil {
|
||||
p.Reader.Close()
|
||||
}
|
||||
return errors.New("packet queue closed")
|
||||
}
|
||||
|
||||
if h.nomore {
|
||||
return errors.New("h.reader already exists")
|
||||
}
|
||||
if p.Reader != nil {
|
||||
h.nomore = true
|
||||
}
|
||||
h.pushedPackets <- p
|
||||
return nil
|
||||
}
|
||||
@@ -65,9 +62,20 @@ func (h *uploadQueue) Close() error {
|
||||
|
||||
if !h.closed {
|
||||
h.closed = true
|
||||
runtime.Gosched() // hope Read() gets the packet
|
||||
f:
|
||||
for {
|
||||
select {
|
||||
case p := <-h.pushedPackets:
|
||||
if p.Reader != nil {
|
||||
h.reader = p.Reader
|
||||
}
|
||||
default:
|
||||
break f
|
||||
}
|
||||
}
|
||||
close(h.pushedPackets)
|
||||
}
|
||||
runtime.Gosched()
|
||||
if h.reader != nil {
|
||||
return h.reader.Close()
|
||||
}
|
||||
|
@@ -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 UnixListenerWrapper struct {
|
||||
*net.UnixListener
|
||||
locker *FileLocker
|
||||
}
|
||||
|
||||
func (l *UnixListenerWrapper) Accept() (net.Conn, error) {
|
||||
conn, err := l.UnixListener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UnixConnWrapper{UnixConn: conn.(*net.UnixConn)}, nil
|
||||
}
|
||||
|
||||
func (l *UnixListenerWrapper) Close() error {
|
||||
if l.locker != nil {
|
||||
l.locker.Release()
|
||||
l.locker = nil
|
||||
}
|
||||
return l.UnixListener.Close()
|
||||
}
|
||||
|
||||
type UnixConnWrapper struct {
|
||||
*net.UnixConn
|
||||
}
|
||||
|
||||
func (conn *UnixConnWrapper) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: []byte{0, 0, 0, 0},
|
||||
}
|
||||
}
|
||||
|
||||
func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (l net.Listener, err error) {
|
||||
var lc net.ListenConfig
|
||||
var network, address string
|
||||
@@ -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 = &UnixListenerWrapper{UnixListener: l.(*net.UnixListener), 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,20 +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 len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "http/1.1" {
|
||||
if err := conn.(*tls.UConn).WebsocketHandshakeContext(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 {
|
||||
if err := conn.(*tls.UConn).HandshakeContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 {
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -277,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
|
||||
}
|
||||
@@ -288,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
|
||||
}
|
||||
@@ -305,7 +331,10 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
|
||||
}
|
||||
|
||||
type RandCarrier struct {
|
||||
ServerNameToVerify string
|
||||
RootCAs *x509.CertPool
|
||||
VerifyPeerCertInNames []string
|
||||
PinnedPeerCertificateChainSha256 [][]byte
|
||||
PinnedPeerCertificatePublicKeySha256 [][]byte
|
||||
}
|
||||
|
||||
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
||||
@@ -329,16 +358,25 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
}
|
||||
}
|
||||
|
||||
randCarrier := &RandCarrier{
|
||||
RootCAs: root,
|
||||
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
|
||||
PinnedPeerCertificateChainSha256: c.PinnedPeerCertificateChainSha256,
|
||||
PinnedPeerCertificatePublicKeySha256: c.PinnedPeerCertificatePublicKeySha256,
|
||||
}
|
||||
config := &tls.Config{
|
||||
Rand: &RandCarrier{
|
||||
ServerNameToVerify: c.ServerNameToVerify,
|
||||
},
|
||||
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,20 +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 one of certs in verified chain matches one of these values, the connection will be eventually accepted.
|
||||
// @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"`
|
||||
ServerNameToVerify string `protobuf:"bytes,17,opt,name=server_name_to_verify,json=serverNameToVerify,proto3" json:"server_name_to_verify,omitempty"`
|
||||
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() {
|
||||
@@ -353,11 +354,11 @@ func (x *Config) GetCurvePreferences() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetServerNameToVerify() string {
|
||||
func (x *Config) GetVerifyPeerCertInNames() []string {
|
||||
if x != nil {
|
||||
return x.ServerNameToVerify
|
||||
return x.VerifyPeerCertInNames
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||
@@ -391,7 +392,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
||||
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, 0x93, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
||||
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x9a, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
||||
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,
|
||||
@@ -437,18 +438,19 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12, 0x2b,
|
||||
0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
||||
0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76, 0x65,
|
||||
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x76, 0x65,
|
||||
0x72, 0x69, 0x66, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76,
|
||||
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x6f, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 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 one of certs in verified chain matches one of these values, the connection will be eventually accepted.
|
||||
@Critical
|
||||
*/
|
||||
repeated bytes pinned_peer_certificate_public_key_sha256 = 14;
|
||||
@@ -88,5 +86,9 @@ message Config {
|
||||
// Lists of string as CurvePreferences values.
|
||||
repeated string curve_preferences = 16;
|
||||
|
||||
string server_name_to_verify = 17;
|
||||
/* @Document Replaces server_name to verify the peer cert.
|
||||
@Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||
@Critical
|
||||
*/
|
||||
repeated string verify_peer_cert_in_names = 17;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ type Interface interface {
|
||||
net.Conn
|
||||
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
|
||||
@@ -134,17 +127,13 @@ func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) ne
|
||||
}
|
||||
|
||||
func copyConfig(c *tls.Config) *utls.Config {
|
||||
serverNameToVerify := ""
|
||||
if r, ok := c.Rand.(*RandCarrier); ok {
|
||||
serverNameToVerify = r.ServerNameToVerify
|
||||
}
|
||||
return &utls.Config{
|
||||
RootCAs: c.RootCAs,
|
||||
ServerName: c.ServerName,
|
||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
||||
KeyLogWriter: c.KeyLogWriter,
|
||||
InsecureServerNameToVerify: serverNameToVerify,
|
||||
Rand: c.Rand,
|
||||
RootCAs: c.RootCAs,
|
||||
ServerName: c.ServerName,
|
||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
||||
KeyLogWriter: c.KeyLogWriter,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +151,14 @@ func init() {
|
||||
weights := utls.DefaultWeights
|
||||
weights.TLSVersMax_Set_VersionTLS13 = 1
|
||||
weights.FirstKeyShare_Set_CurveP256 = 0
|
||||
randomized := utls.HelloRandomized
|
||||
randomized := utls.HelloRandomizedALPN
|
||||
randomized.Seed, _ = utls.NewPRNGSeed()
|
||||
randomized.Weights = &weights
|
||||
randomizednoalpn := utls.HelloRandomizedNoALPN
|
||||
randomizednoalpn.Seed, _ = utls.NewPRNGSeed()
|
||||
randomizednoalpn.Weights = &weights
|
||||
PresetFingerprints["randomized"] = &randomized
|
||||
PresetFingerprints["randomizednoalpn"] = &randomizednoalpn
|
||||
}
|
||||
|
||||
func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) {
|
||||
@@ -186,17 +179,18 @@ func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) {
|
||||
|
||||
var PresetFingerprints = map[string]*utls.ClientHelloID{
|
||||
// Recommended preset options in GUI clients
|
||||
"chrome": &utls.HelloChrome_Auto,
|
||||
"firefox": &utls.HelloFirefox_Auto,
|
||||
"safari": &utls.HelloSafari_Auto,
|
||||
"ios": &utls.HelloIOS_Auto,
|
||||
"android": &utls.HelloAndroid_11_OkHttp,
|
||||
"edge": &utls.HelloEdge_Auto,
|
||||
"360": &utls.Hello360_Auto,
|
||||
"qq": &utls.HelloQQ_Auto,
|
||||
"random": nil,
|
||||
"randomized": nil,
|
||||
"unsafe": nil,
|
||||
"chrome": &utls.HelloChrome_Auto,
|
||||
"firefox": &utls.HelloFirefox_Auto,
|
||||
"safari": &utls.HelloSafari_Auto,
|
||||
"ios": &utls.HelloIOS_Auto,
|
||||
"android": &utls.HelloAndroid_11_OkHttp,
|
||||
"edge": &utls.HelloEdge_Auto,
|
||||
"360": &utls.Hello360_Auto,
|
||||
"qq": &utls.HelloQQ_Auto,
|
||||
"random": nil,
|
||||
"randomized": nil,
|
||||
"randomizednoalpn": nil,
|
||||
"unsafe": nil,
|
||||
}
|
||||
|
||||
var ModernFingerprints = map[string]*utls.ClientHelloID{
|
||||
@@ -204,12 +198,14 @@ var ModernFingerprints = map[string]*utls.ClientHelloID{
|
||||
"hellofirefox_99": &utls.HelloFirefox_99,
|
||||
"hellofirefox_102": &utls.HelloFirefox_102,
|
||||
"hellofirefox_105": &utls.HelloFirefox_105,
|
||||
"hellofirefox_120": &utls.HelloFirefox_120,
|
||||
"hellochrome_83": &utls.HelloChrome_83,
|
||||
"hellochrome_87": &utls.HelloChrome_87,
|
||||
"hellochrome_96": &utls.HelloChrome_96,
|
||||
"hellochrome_100": &utls.HelloChrome_100,
|
||||
"hellochrome_102": &utls.HelloChrome_102,
|
||||
"hellochrome_106_shuffle": &utls.HelloChrome_106_Shuffle,
|
||||
"hellochrome_120": &utls.HelloChrome_120,
|
||||
"helloios_13": &utls.HelloIOS_13,
|
||||
"helloios_14": &utls.HelloIOS_14,
|
||||
"helloedge_85": &utls.HelloEdge_85,
|
||||
|
Reference in New Issue
Block a user