Compare commits

...

32 Commits

Author SHA1 Message Date
RPRX
4c82ef8a1b v1.8.23 2024-07-29 10:23:04 +00:00
mmmray
30af792777 SplitHTTP: Rename three options & Reduce server defaults (#3611)
* maxUploadSize -> scMaxEachPostBytes, default is 1MB on both sides (was 2MB on the server)
* minUploadIntervalMs -> scMinPostsIntervalMs, default is 30ms on the client (no server support for now)
* maxConcurrentUploads -> scMaxConcurrentPosts, default is 100 on both sides (was 200 on the server)
2024-07-29 10:10:29 +00:00
mmmray
33daa0c94b SplitHTTP: Fix wrong config deserialization (#3610)
Testing was conducted only using explicit parameters, and using
testsuite. However, when the parameters are not explicitly set in JSON
config, it seems that `c.MaxUploadSize` will contain `RandRangeConfig {
From: 0, To: 0 }` instead of `nil`, which breaks upload entirely.
2024-07-29 08:50:59 +00:00
RPRX
70383c50cc v1.8.22 2024-07-29 08:22:12 +00:00
mayampi01
521d8ef6a1 Docker: Add multi-arch builds and Loyalsoldier flavor (#3589)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2024-07-29 07:36:30 +00:00
lelemka0
4531a7e228 CertificateObject: Enable auto-reload for cacert & Add buildChain & Fixes (#3607) 2024-07-29 06:58:58 +00:00
yuhan6665
a342db3e28 Fix a nil pointer in Wireguard client logging (#3608) 2024-07-29 06:43:46 +00:00
RPRX
60553a6c26 SplitHTTP server: Add noSSEHeader
https://github.com/XTLS/Xray-core/pull/3603#issuecomment-2254968219
2024-07-29 06:32:04 +00:00
mmmray
59f6685774 SplitHTTP: More range options, change defaults, enforce maxUploadSize, fix querystring behavior (#3603)
* maxUploadSize and maxConcurrentUploads can now be ranges on the client
* maxUploadSize is now enforced on the server
* the default of maxUploadSize is 2MB on the server, and 1MB on the client
* the default of maxConcurrentUploads is 200 on the server, and 100 on the client
* ranges on the server are treated as a single number. if server is configured as `"1-2"`, server will enforce `2`
* querystrings in `path` are now handled correctly
2024-07-29 04:35:17 +00:00
yuhan6665
4cb2a128db Don't do raw/splice copy in case of MITM 2024-07-24 20:41:40 -04:00
mmmray
8a4217fdf5 SplitHTTP client: Add minUploadInterval (#3592) 2024-07-27 12:52:36 +00:00
mmmray
7cf5ee8afd WS, HU: Remove unnecessary sleep from test (#3600) 2024-07-27 12:38:54 +00:00
mmmray
2becdd6414 SplitHTTP server: Fix panic during concurrent Close and Push (#3593)
When Close and Push are called concurrently, it may happen that Push attempts to write to an already-closed channel, and trigger a panic.

From a user perspective, it results in logs like this:

    http: panic serving 172.19.0.6:50476: send on closed channel

It's probably triggered when download is closed at the same time an upload packet is submitted.

These panics don't crash the server and the inbound is still usable.
2024-07-26 04:36:55 +02:00
hellokindle
edae38c620 Fix SplitHTTP Unix domain socket (#3577)
Co-authored-by: mmmray <142015632+mmmray@users.noreply.github.com>
2024-07-22 22:19:31 +02:00
mayampi01
36f427f22b docker.yml: Set latest to auto to tag release version (#3581) 2024-07-22 16:16:25 +02:00
RPRX
c27d652d80 v1.8.21 2024-07-21 21:32:26 +00:00
RPRX
0f65aa8ed8 Fix SplitHTTP H3 waited for downResponse before uploading
https://github.com/XTLS/Xray-core/issues/3560#issuecomment-2241750579
2024-07-21 20:45:05 +00:00
RPRX
22535d8643 Fix SplitHTTP H3 didn't always reuse QUIC connection
https://github.com/XTLS/Xray-core/issues/3560#issuecomment-2241531502
2024-07-21 08:55:03 +00:00
mmmray
529f206d33 Fix serverside TLS support of SplitHTTP H1/H2 (#3567)
Fix #3566

Also update testsuite so that all tests read and write some data. Opening a connection is not enough to trigger connection errors, because the connection is so lazy.
2024-07-20 19:35:24 -05:00
チセ
964859b4bc SplitHTTP: Remove unnecessary keepalives (#3565)
Remove keep alive since quic-go/http3 doesn't support stream reuse
Discussion see https://t.me/projectXray/3782492

Co-authored-by: Fangliding <Fangliding.fshxy@outlook.com>
Co-authored-by: xqzr <34030394+xqzr@users.noreply.github.com>
Co-authored-by: ll11l1lIllIl1lll <88377095+ll11l1lIllIl1lll@users.noreply.github.com>
2024-07-20 19:34:57 -05:00
RPRX
8deb953aec v1.8.20 2024-07-20 06:10:40 +00:00
ll11l1lIllIl1lll
a0040f13dd SplitHTTP: Server supports HTTP/3 (#3554)
Co-authored-by: mmmray <142015632+mmmray@users.noreply.github.com>
2024-07-19 17:53:47 +00:00
ll11l1lIllIl1lll
d8994b7603 Fix SplitHTTP H3 crash on v2rayNG (#3559)
Fixes https://github.com/XTLS/Xray-core/issues/3556
2024-07-19 17:52:34 +00:00
RPRX
b277bacdf6 v1.8.19 2024-07-17 13:51:21 +00:00
RPRX
9288a7c0dc Upgrade dependencies 2024-07-17 13:45:16 +00:00
ll11l1lIllIl1lll
c40fc44a34 SplitHTTP: Client supports HTTP/3 (#3543)
Closes https://github.com/XTLS/Xray-core/issues/3456

Co-authored-by: Fangliding <Fangliding.fshxy@outlook.com>
Co-authored-by: mmmray <142015632+mmmray@users.noreply.github.com>
2024-07-17 07:55:28 -05:00
yuhan6665
02cd3b8c74 Fix SplitHTTP race condition when creating new sessions (#3533)
Co-authored-by: nobody <nobody@nowhere.mars>
Co-authored-by: mmmray <142015632+mmmray@users.noreply.github.com>
2024-07-17 11:41:17 +00:00
风扇滑翔翼
a7e198e1e2 Fix WS reading X-Forwarded-For & Add tests (#3546)
Fixes https://github.com/XTLS/Xray-core/issues/3545

---------

Co-authored-by: mmmray <142015632+mmmray@users.noreply.github.com>
2024-07-17 10:40:25 +00:00
RPRX
9e6d7a3cb0 v1.8.18 2024-07-15 13:57:18 +00:00
RPRX
a4bc422ed1 README: Remove 13 inactive projects 2024-07-15 13:53:43 +00:00
abvf
59819e2a1b Fix log severity (#3535) 2024-07-15 14:04:04 +02:00
mmmray
573fb4f643 Add Windows 7 builds (#3530)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2024-07-15 09:53:40 +00:00
30 changed files with 999 additions and 329 deletions

View File

@@ -2,21 +2,27 @@
FROM --platform=$BUILDPLATFORM golang:alpine AS build
WORKDIR /src
COPY . .
ARG TARGETOS TARGETARCH
ARG TARGETOS
ARG TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
ADD https://github.com/v2fly/geoip/releases/latest/download/geoip.dat /v2fly/geoip.dat
ADD https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat /v2fly/geosite.dat
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat /loyalsoldier/geoip.dat
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat /loyalsoldier/geosite.dat
FROM --platform=${TARGETPLATFORM} alpine:latest
WORKDIR /root
# chainguard/static contains only tzdata and ca-certificates, can be built with multiarch static binaries.
FROM --platform=linux/amd64 chainguard/static:latest
WORKDIR /var/log/xray
COPY .github/docker/files/config.json /etc/xray/config.json
COPY --from=build /src/xray /usr/bin/xray
RUN set -ex \
&& apk add --no-cache tzdata ca-certificates \
&& mkdir -p /var/log/xray /usr/share/xray \
&& chmod +x /usr/bin/xray \
&& wget -O /usr/share/xray/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat \
&& wget -O /usr/share/xray/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
COPY --from=build --chmod=755 /src/xray /usr/bin/xray
USER root
WORKDIR /root
VOLUME /etc/xray
ENV TZ=Asia/Shanghai
ARG TZ=Asia/Shanghai
ENV TZ=$TZ
ENTRYPOINT [ "/usr/bin/xray" ]
CMD [ "-config", "/etc/xray/config.json" ]
ARG flavor=v2fly
COPY --from=build /$flavor /usr/share/xray

View File

@@ -19,7 +19,20 @@ jobs:
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/xray-core
flavor: latest=true
flavor: latest=auto
tags: |
type=sha
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
- name: Docker metadata Loyalsoldier flavor
id: loyalsoldier
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/xray-core
flavor: |
latest=auto
suffix=-ls,onlatest=true
tags: |
type=sha
type=ref,event=branch
@@ -31,18 +44,33 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- # Add support for more platforms with QEMU (optional)
# https://github.com/docker/setup-qemu-action
name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
platforms: |
linux/amd64
linux/arm64
linux/loong64
linux/riscv64
provenance: false
file: .github/docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Build and push Loyalsoldier flavor
uses: docker/build-push-action@v6
with:
context: .
platforms: |
linux/amd64
linux/arm64
linux/loong64
linux/riscv64
provenance: false
file: .github/docker/Dockerfile
build-args: flavor=loyalsoldier
push: true
tags: |
${{ steps.loyalsoldier.outputs.tags }}

View File

@@ -78,6 +78,9 @@ jobs:
# Include amd64 on all platforms.
goos: [windows, freebsd, openbsd, linux, darwin]
goarch: [amd64, 386]
gotoolchain: [""]
patch-assetname: [""]
exclude:
# Exclude i386 on darwin
- goarch: 386
@@ -152,6 +155,16 @@ jobs:
goarch: arm
goarm: 7
# END OPENBSD ARM
# BEGIN Windows 7
- goos: windows
goarch: amd64
gotoolchain: 1.21.4
patch-assetname: win7-64
- goos: windows
goarch: 386
gotoolchain: 1.21.4
patch-assetname: win7-32
# END Windows 7
fail-fast: false
runs-on: ubuntu-latest
@@ -164,16 +177,17 @@ jobs:
- name: Checkout codebase
uses: actions/checkout@v4
- name: Show workflow information
- name: Show workflow information
run: |
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
_NAME=${{ matrix.patch-assetname }}
[ -n "$_NAME" ] || _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
go-version: ${{ matrix.gotoolchain || '1.22' }}
check-latest: true
- name: Get project dependencies

View File

@@ -32,7 +32,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
go-version: '1.22'
check-latest: true
- name: Restore Cache
uses: actions/cache/restore@v4

View File

@@ -27,8 +27,7 @@
- [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**)
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
- Web Panel
- [X-UI-English](https://github.com/NidukaAkalanka/x-ui-english), [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [X-UI](https://github.com/diditra/x-ui)
- [Xray-UI](https://github.com/qist/xray-ui), [X-UI](https://github.com/sing-web/x-ui)
- [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [Xray-UI](https://github.com/qist/xray-ui)
- [Hiddify](https://github.com/hiddify/hiddify-config)
- [Marzban](https://github.com/Gozargah/Marzban)
- [Libertea](https://github.com/VZiChoushaDui/Libertea)
@@ -67,13 +66,10 @@
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
- Windows
- [v2rayN](https://github.com/2dust/v2rayN)
- [NekoRay](https://github.com/Matsuridayo/nekoray)
- [Furious](https://github.com/LorenEteval/Furious)
- [HiddifyN](https://github.com/hiddify/HiddifyN)
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
- Android
- [v2rayNG](https://github.com/2dust/v2rayNG)
- [HiddifyNG](https://github.com/hiddify/HiddifyNG)
- [X-flutter](https://github.com/XTLS/X-flutter)
- iOS & macOS arm64
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
@@ -85,7 +81,6 @@
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
- Linux
- [v2rayA](https://github.com/v2rayA/v2rayA)
- [NekoRay](https://github.com/Matsuridayo/nekoray)
- [Furious](https://github.com/LorenEteval/Furious)
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
@@ -98,21 +93,15 @@
- [XTLS/libXray](https://github.com/XTLS/libXray)
- [xtlsapi](https://github.com/hiddify/xtlsapi)
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
- [XrayKit](https://github.com/arror/XrayKit)
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
- [xray-api](https://github.com/XVGuardian/xray-api)
- [XrayR](https://github.com/XrayR-project/XrayR)
- [XrayR-release](https://github.com/XrayR-project/XrayR-release)
- [XrayR-V2Board](https://github.com/missuo/XrayR-V2Board)
- [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta)
- [Clash Verge](https://github.com/zzzgydi/clash-verge)
- [clashN](https://github.com/2dust/clashN)
- [Clash Meta for Android](https://github.com/MetaCubeX/ClashMetaForAndroid)
- [meta_for_ios](https://t.me/meta_for_ios)
- [sing-box](https://github.com/SagerNet/sing-box)
- [installReality](https://github.com/BoxXt/installReality)
- [sbox-reality](https://github.com/Misaka-blog/sbox-reality)
- [sing-box-for-ios](https://github.com/SagerNet/sing-box-for-ios)
## Contributing

View File

@@ -151,7 +151,7 @@ func LogInfo(ctx context.Context, msg ...interface{}) {
}
func LogInfoInner(ctx context.Context, inner error, msg ...interface{}) {
doLog(ctx, inner, log.Severity_Debug, msg...)
doLog(ctx, inner, log.Severity_Info, msg...)
}
func LogWarning(ctx context.Context, msg ...interface{}) {
@@ -159,7 +159,7 @@ func LogWarning(ctx context.Context, msg ...interface{}) {
}
func LogWarningInner(ctx context.Context, inner error, msg ...interface{}) {
doLog(ctx, inner, log.Severity_Debug, msg...)
doLog(ctx, inner, log.Severity_Warning, msg...)
}
func LogError(ctx context.Context, msg ...interface{}) {
@@ -167,7 +167,7 @@ func LogError(ctx context.Context, msg ...interface{}) {
}
func LogErrorInner(ctx context.Context, inner error, msg ...interface{}) {
doLog(ctx, inner, log.Severity_Debug, msg...)
doLog(ctx, inner, log.Severity_Error, msg...)
}
func doLog(ctx context.Context, inner error, severity log.Severity, msg ...interface{}) {

View File

@@ -149,10 +149,6 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) {
BasicConstraintsValid: true,
}
for _, opt := range opts {
opt(template)
}
parentCert := template
if parent != nil {
pCert, err := x509.ParseCertificate(parent.Certificate)
@@ -162,6 +158,17 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) {
parentCert = pCert
}
if parentCert.NotAfter.Before(template.NotAfter) {
template.NotAfter = parentCert.NotAfter
}
if parentCert.NotBefore.After(template.NotBefore) {
template.NotBefore = parentCert.NotBefore
}
for _, opt := range opts {
opt(template)
}
derBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, publicKey(selfKey), parentKey)
if err != nil {
return nil, errors.New("failed to create certificate").Base(err)

View File

@@ -21,7 +21,7 @@ import (
var (
Version_x byte = 1
Version_y byte = 8
Version_z byte = 17
Version_z byte = 23
)
var (

5
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/xtls/xray-core
go 1.22
go 1.21.4
require (
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
@@ -16,7 +16,7 @@ require (
github.com/refraction-networking/utls v1.6.7
github.com/sagernet/sing v0.4.1
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
github.com/stretchr/testify v1.9.0
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3
@@ -46,6 +46,7 @@ require (
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
go.uber.org/mock v0.4.0 // indirect

6
go.sum
View File

@@ -110,6 +110,8 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA=
github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
@@ -121,8 +123,8 @@ github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=

View File

@@ -2,6 +2,7 @@ package conf
import (
"encoding/json"
"strconv"
"strings"
"github.com/xtls/xray-core/common/errors"
@@ -242,3 +243,33 @@ func (v *User) Build() *protocol.User {
Level: uint32(v.LevelByte),
}
}
// Int32Range deserializes from "1-2" or 1, so can deserialize from both int and number.
// Negative integers can be passed as sentinel values, but do not parse as ranges.
type Int32Range struct {
From int32
To int32
}
func (v *Int32Range) UnmarshalJSON(data []byte) error {
var stringrange string
var rawint int32
if err := json.Unmarshal(data, &stringrange); err == nil {
pair := strings.SplitN(stringrange, "-", 2)
if len(pair) == 2 {
from, err := strconv.Atoi(pair[0])
to, err2 := strconv.Atoi(pair[1])
if err == nil && err2 == nil {
v.From = int32(from)
v.To = int32(to)
return nil
}
}
} else if err := json.Unmarshal(data, &rawint); err == nil {
v.From = rawint
v.To = rawint
return nil
}
return errors.New("Invalid integer range, expected either string of form \"1-2\" or plain integer.")
}

View File

@@ -229,8 +229,10 @@ type SplitHTTPConfig struct {
Host string `json:"host"`
Path string `json:"path"`
Headers map[string]string `json:"headers"`
MaxConcurrentUploads int32 `json:"maxConcurrentUploads"`
MaxUploadSize int32 `json:"maxUploadSize"`
ScMaxConcurrentPosts Int32Range `json:"scMaxConcurrentPosts"`
ScMaxEachPostBytes Int32Range `json:"scMaxEachPostBytes"`
ScMinPostsIntervalMs Int32Range `json:"scMinPostsIntervalMs"`
NoSSEHeader bool `json:"noSSEHeader"`
}
// Build implements Buildable.
@@ -244,11 +246,22 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
c.Host = c.Headers["Host"]
}
config := &splithttp.Config{
Path: c.Path,
Host: c.Host,
Header: c.Headers,
MaxConcurrentUploads: c.MaxConcurrentUploads,
MaxUploadSize: c.MaxUploadSize,
Path: c.Path,
Host: c.Host,
Header: c.Headers,
ScMaxConcurrentPosts: &splithttp.RandRangeConfig{
From: c.ScMaxConcurrentPosts.From,
To: c.ScMaxConcurrentPosts.To,
},
ScMaxEachPostBytes: &splithttp.RandRangeConfig{
From: c.ScMaxEachPostBytes.From,
To: c.ScMaxEachPostBytes.To,
},
ScMinPostsIntervalMs: &splithttp.RandRangeConfig{
From: c.ScMinPostsIntervalMs.From,
To: c.ScMinPostsIntervalMs.To,
},
NoSSEHeader: c.NoSSEHeader,
}
return config, nil
}
@@ -372,6 +385,7 @@ type TLSCertConfig struct {
Usage string `json:"usage"`
OcspStapling uint64 `json:"ocspStapling"`
OneTimeLoading bool `json:"oneTimeLoading"`
BuildChain bool `json:"buildChain"`
}
// Build implements Buildable.
@@ -410,6 +424,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
certificate.OneTimeLoading = c.OneTimeLoading
}
certificate.OcspStapling = c.OcspStapling
certificate.BuildChain = c.BuildChain
return certificate, nil
}

View File

@@ -28,6 +28,7 @@ import (
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
)
var useSplice bool
@@ -225,9 +226,16 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
writeConn = inbound.Conn
inTimer = inbound.Timer
}
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
if !isTLSConn(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
}
}
var reader buf.Reader
if destination.Network == net.Network_TCP {
reader = buf.NewReader(conn)
} else {
reader = NewPacketReader(conn, UDPOverride)
}
reader := NewPacketReader(conn, UDPOverride)
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to process response").Base(err)
}
@@ -245,6 +253,19 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
return nil
}
func isTLSConn(conn stat.Connection) bool {
if conn != nil {
statConn, ok := conn.(*stat.CounterConnection)
if ok {
conn = statConn.Connection
}
if _, ok := conn.(*tls.Conn); ok {
return true
}
}
return false
}
func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
iConn := conn
statConn, ok := iConn.(*stat.CounterConnection)

View File

@@ -100,7 +100,7 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
}
// bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer
bind := &netBindClient{
h.bind = &netBindClient{
netBind: netBind{
dns: h.dns,
dnsOption: dns.IPOption{
@@ -115,15 +115,14 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
}
defer func() {
if err != nil {
_ = bind.Close()
_ = h.bind.Close()
}
}()
h.net, err = h.makeVirtualTun(bind)
h.net, err = h.makeVirtualTun()
if err != nil {
return errors.New("failed to create virtual tun interface").Base(err)
}
h.bind = bind
return nil
}
@@ -238,16 +237,16 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
}
// creates a tun interface on netstack given a configuration
func (h *Handler) makeVirtualTun(bind *netBindClient) (Tunnel, error) {
func (h *Handler) makeVirtualTun() (Tunnel, error) {
t, err := h.conf.createTun()(h.endpoints, int(h.conf.Mtu), nil)
if err != nil {
return nil, err
}
bind.dnsOption.IPv4Enable = h.hasIPv4
bind.dnsOption.IPv6Enable = h.hasIPv6
h.bind.dnsOption.IPv4Enable = h.hasIPv4
h.bind.dnsOption.IPv6Enable = h.hasIPv6
if err = t.BuildDevice(h.createIPCRequest(bind, h.conf), bind); err != nil {
if err = t.BuildDevice(h.createIPCRequest(), h.bind); err != nil {
_ = t.Close()
return nil, err
}
@@ -255,17 +254,17 @@ func (h *Handler) makeVirtualTun(bind *netBindClient) (Tunnel, error) {
}
// serialize the config into an IPC request
func (h *Handler) createIPCRequest(bind *netBindClient, conf *DeviceConfig) string {
func (h *Handler) createIPCRequest() string {
var request strings.Builder
request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey))
request.WriteString(fmt.Sprintf("private_key=%s\n", h.conf.SecretKey))
if !conf.IsClient {
if !h.conf.IsClient {
// placeholder, we'll handle actual port listening on Xray
request.WriteString("listen_port=1337\n")
}
for _, peer := range conf.Peers {
for _, peer := range h.conf.Peers {
if peer.PublicKey != "" {
request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey))
}
@@ -280,7 +279,7 @@ func (h *Handler) createIPCRequest(bind *netBindClient, conf *DeviceConfig) stri
}
addr := net.ParseAddress(address)
if addr.Family().IsDomain() {
dialerIp := bind.dialer.DestIpAddress()
dialerIp := h.bind.dialer.DestIpAddress()
if dialerIp != nil {
addr = net.ParseAddress(dialerIp.String())
errors.LogInfo(h.bind.ctx, "createIPCRequest use dialer dest ip: ", addr)

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.2
// protoc v5.27.2
// protoc-gen-go v1.34.1
// protoc v5.27.0
// source: transport/internet/config.proto
package internet
@@ -863,7 +863,7 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_transport_internet_config_proto_goTypes = []any{
var file_transport_internet_config_proto_goTypes = []interface{}{
(TransportProtocol)(0), // 0: xray.transport.internet.TransportProtocol
(DomainStrategy)(0), // 1: xray.transport.internet.DomainStrategy
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
@@ -897,7 +897,7 @@ func file_transport_internet_config_proto_init() {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
file_transport_internet_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TransportConfig); i {
case 0:
return &v.state
@@ -909,7 +909,7 @@ func file_transport_internet_config_proto_init() {
return nil
}
}
file_transport_internet_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
file_transport_internet_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StreamConfig); i {
case 0:
return &v.state
@@ -921,7 +921,7 @@ func file_transport_internet_config_proto_init() {
return nil
}
}
file_transport_internet_config_proto_msgTypes[2].Exporter = func(v any, i int) any {
file_transport_internet_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ProxyConfig); i {
case 0:
return &v.state
@@ -933,7 +933,7 @@ func file_transport_internet_config_proto_init() {
return nil
}
}
file_transport_internet_config_proto_msgTypes[3].Exporter = func(v any, i int) any {
file_transport_internet_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CustomSockopt); i {
case 0:
return &v.state
@@ -945,7 +945,7 @@ func file_transport_internet_config_proto_init() {
return nil
}
}
file_transport_internet_config_proto_msgTypes[4].Exporter = func(v any, i int) any {
file_transport_internet_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SocketConfig); i {
case 0:
return &v.state

View File

@@ -58,7 +58,6 @@ func Test_listenHTTPUpgradeAndDial(t *testing.T) {
}
common.Must(conn.Close())
<-time.After(time.Second * 5)
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 2"))
@@ -118,7 +117,6 @@ func Test_listenHTTPUpgradeAndDialWithHeaders(t *testing.T) {
}
common.Must(conn.Close())
<-time.After(time.Second * 5)
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 2"))
@@ -151,7 +149,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
return
}
_, err = c.Write([]byte("Response"))
_, err = c.Write([]byte(c.RemoteAddr().String()))
common.Must(err)
}(conn)
})
@@ -169,7 +167,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
var b [1024]byte
n, err := conn.Read(b[:])
common.Must(err)
if string(b[:n]) != "Response" {
if string(b[:n]) != "1.1.1.1:0" {
t.Error("response: ", string(b[:n]))
}

View File

@@ -32,6 +32,7 @@ type DefaultDialerClient struct {
download *http.Client
upload *http.Client
isH2 bool
isH3 bool
// pool of net.Conn, created using dialUploadConn
uploadRawPool *sync.Pool
dialUploadConn func(ctxInner context.Context) (net.Conn, error)
@@ -93,6 +94,10 @@ func (c *DefaultDialerClient) OpenDownload(ctx context.Context, baseURL string)
gotDownResponse.Close()
}()
if c.isH3 {
gotConn.Close()
}
// we want to block Dial until we know the remote address of the server,
// for logging purposes
<-gotConn.Wait()
@@ -112,13 +117,13 @@ func (c *DefaultDialerClient) OpenDownload(ctx context.Context, baseURL string)
func (c *DefaultDialerClient) SendUploadRequest(ctx context.Context, url string, payload io.ReadWriteCloser, contentLength int64) error {
req, err := http.NewRequest("POST", url, payload)
req.ContentLength = contentLength
if err != nil {
return err
}
req.ContentLength = contentLength
req.Header = c.transportConfig.GetRequestHeader()
if c.isH2 {
if c.isH2 || c.isH3 {
resp, err := c.upload.Do(req)
if err != nil {
return err

View File

@@ -1,24 +1,31 @@
package splithttp
import (
"crypto/rand"
"math/big"
"net/http"
"strings"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/transport/internet"
)
func (c *Config) GetNormalizedPath() string {
path := c.Path
if path == "" {
path = "/"
func (c *Config) GetNormalizedPath(addPath string, addQuery bool) string {
pathAndQuery := strings.SplitN(c.Path, "?", 2)
path := pathAndQuery[0]
query := ""
if len(pathAndQuery) > 1 && addQuery {
query = "?" + pathAndQuery[1]
}
if path[0] != '/' {
if path == "" || path[0] != '/' {
path = "/" + path
}
if path[len(path)-1] != '/' {
path = path + "/"
}
return path
return path + addPath + query
}
func (c *Config) GetRequestHeader() http.Header {
@@ -29,20 +36,37 @@ func (c *Config) GetRequestHeader() http.Header {
return header
}
func (c *Config) GetNormalizedMaxConcurrentUploads() int32 {
if c.MaxConcurrentUploads == 0 {
return 10
func (c *Config) GetNormalizedScMaxConcurrentPosts() RandRangeConfig {
if c.ScMaxConcurrentPosts == nil || c.ScMaxConcurrentPosts.To == 0 {
return RandRangeConfig{
From: 100,
To: 100,
}
}
return c.MaxConcurrentUploads
return *c.ScMaxConcurrentPosts
}
func (c *Config) GetNormalizedMaxUploadSize() int32 {
if c.MaxUploadSize == 0 {
return 1000000
func (c *Config) GetNormalizedScMaxEachPostBytes() RandRangeConfig {
if c.ScMaxEachPostBytes == nil || c.ScMaxEachPostBytes.To == 0 {
return RandRangeConfig{
From: 1000000,
To: 1000000,
}
}
return c.MaxUploadSize
return *c.ScMaxEachPostBytes
}
func (c *Config) GetNormalizedScMinPostsIntervalMs() RandRangeConfig {
if c.ScMinPostsIntervalMs == nil || c.ScMinPostsIntervalMs.To == 0 {
return RandRangeConfig{
From: 30,
To: 30,
}
}
return *c.ScMinPostsIntervalMs
}
func init() {
@@ -50,3 +74,11 @@ func init() {
return new(Config)
}))
}
func (c RandRangeConfig) roll() 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())
}

View File

@@ -28,8 +28,10 @@ type Config struct {
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
Header map[string]string `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
MaxConcurrentUploads int32 `protobuf:"varint,4,opt,name=maxConcurrentUploads,proto3" json:"maxConcurrentUploads,omitempty"`
MaxUploadSize int32 `protobuf:"varint,5,opt,name=maxUploadSize,proto3" json:"maxUploadSize,omitempty"`
ScMaxConcurrentPosts *RandRangeConfig `protobuf:"bytes,4,opt,name=scMaxConcurrentPosts,proto3" json:"scMaxConcurrentPosts,omitempty"`
ScMaxEachPostBytes *RandRangeConfig `protobuf:"bytes,5,opt,name=scMaxEachPostBytes,proto3" json:"scMaxEachPostBytes,omitempty"`
ScMinPostsIntervalMs *RandRangeConfig `protobuf:"bytes,6,opt,name=scMinPostsIntervalMs,proto3" json:"scMinPostsIntervalMs,omitempty"`
NoSSEHeader bool `protobuf:"varint,7,opt,name=noSSEHeader,proto3" json:"noSSEHeader,omitempty"`
}
func (x *Config) Reset() {
@@ -85,16 +87,85 @@ func (x *Config) GetHeader() map[string]string {
return nil
}
func (x *Config) GetMaxConcurrentUploads() int32 {
func (x *Config) GetScMaxConcurrentPosts() *RandRangeConfig {
if x != nil {
return x.MaxConcurrentUploads
return x.ScMaxConcurrentPosts
}
return nil
}
func (x *Config) GetScMaxEachPostBytes() *RandRangeConfig {
if x != nil {
return x.ScMaxEachPostBytes
}
return nil
}
func (x *Config) GetScMinPostsIntervalMs() *RandRangeConfig {
if x != nil {
return x.ScMinPostsIntervalMs
}
return nil
}
func (x *Config) GetNoSSEHeader() bool {
if x != nil {
return x.NoSSEHeader
}
return false
}
type RandRangeConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
From int32 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"`
To int32 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"`
}
func (x *RandRangeConfig) Reset() {
*x = RandRangeConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_splithttp_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RandRangeConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RandRangeConfig) ProtoMessage() {}
func (x *RandRangeConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_splithttp_config_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RandRangeConfig.ProtoReflect.Descriptor instead.
func (*RandRangeConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_splithttp_config_proto_rawDescGZIP(), []int{1}
}
func (x *RandRangeConfig) GetFrom() int32 {
if x != nil {
return x.From
}
return 0
}
func (x *Config) GetMaxUploadSize() int32 {
func (x *RandRangeConfig) GetTo() int32 {
if x != nil {
return x.MaxUploadSize
return x.To
}
return 0
}
@@ -106,8 +177,8 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x22, 0x94,
0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73,
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x22, 0x86,
0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a,
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
0x68, 0x12, 0x4d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28,
@@ -115,25 +186,43 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69,
0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x12, 0x32, 0x0a, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14,
0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x6c,
0x6f, 0x61, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f, 0x61,
0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78,
0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x39, 0x0a, 0x0b, 0x48, 0x65,
0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x85, 0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50,
0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f,
0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79,
0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x12, 0x66, 0x0a, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x5a, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x55,
0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x32, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74,
0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42,
0x79, 0x74, 0x65, 0x73, 0x12, 0x64, 0x0a, 0x13, 0x6d, 0x69, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61,
0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x32, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69,
0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64,
0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x6f,
0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x39, 0x0a, 0x0b,
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x35, 0x0a, 0x0f, 0x52, 0x61, 0x6e, 0x64, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72,
0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e,
0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x74, 0x6f, 0x42, 0x85,
0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73,
0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
0x74, 0x70, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c,
0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -148,18 +237,22 @@ func file_transport_internet_splithttp_config_proto_rawDescGZIP() []byte {
return file_transport_internet_splithttp_config_proto_rawDescData
}
var file_transport_internet_splithttp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_transport_internet_splithttp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_transport_internet_splithttp_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.internet.splithttp.Config
nil, // 1: xray.transport.internet.splithttp.Config.HeaderEntry
(*Config)(nil), // 0: xray.transport.internet.splithttp.Config
(*RandRangeConfig)(nil), // 1: xray.transport.internet.splithttp.RandRangeConfig
nil, // 2: xray.transport.internet.splithttp.Config.HeaderEntry
}
var file_transport_internet_splithttp_config_proto_depIdxs = []int32{
1, // 0: xray.transport.internet.splithttp.Config.header:type_name -> xray.transport.internet.splithttp.Config.HeaderEntry
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
2, // 0: xray.transport.internet.splithttp.Config.header:type_name -> xray.transport.internet.splithttp.Config.HeaderEntry
1, // 1: xray.transport.internet.splithttp.Config.scMaxConcurrentPosts:type_name -> xray.transport.internet.splithttp.RandRangeConfig
1, // 2: xray.transport.internet.splithttp.Config.scMaxEachPostBytes:type_name -> xray.transport.internet.splithttp.RandRangeConfig
1, // 3: xray.transport.internet.splithttp.Config.scMinPostsIntervalMs:type_name -> xray.transport.internet.splithttp.RandRangeConfig
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_transport_internet_splithttp_config_proto_init() }
@@ -180,6 +273,18 @@ func file_transport_internet_splithttp_config_proto_init() {
return nil
}
}
file_transport_internet_splithttp_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RandRangeConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -187,7 +292,7 @@ func file_transport_internet_splithttp_config_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_splithttp_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},

View File

@@ -10,6 +10,13 @@ message Config {
string host = 1;
string path = 2;
map<string, string> header = 3;
int32 maxConcurrentUploads = 4;
int32 maxUploadSize = 5;
RandRangeConfig scMaxConcurrentPosts = 4;
RandRangeConfig scMaxEachPostBytes = 5;
RandRangeConfig scMinPostsIntervalMs = 6;
bool noSSEHeader = 7;
}
message RandRangeConfig {
int32 from = 1;
int32 to = 2;
}

View File

@@ -0,0 +1,51 @@
package splithttp_test
import (
"testing"
. "github.com/xtls/xray-core/transport/internet/splithttp"
)
func Test_GetNormalizedPath(t *testing.T) {
c := Config{
Path: "/?world",
}
path := c.GetNormalizedPath("hello", true)
if path != "/hello?world" {
t.Error("Unexpected: ", path)
}
}
func Test_GetNormalizedPath2(t *testing.T) {
c := Config{
Path: "?world",
}
path := c.GetNormalizedPath("hello", true)
if path != "/hello?world" {
t.Error("Unexpected: ", path)
}
}
func Test_GetNormalizedPath3(t *testing.T) {
c := Config{
Path: "hello?world",
}
path := c.GetNormalizedPath("", true)
if path != "/hello/?world" {
t.Error("Unexpected: ", path)
}
}
func Test_GetNormalizedPath4(t *testing.T) {
c := Config{
Path: "hello?world",
}
path := c.GetNormalizedPath("", false)
if path != "/hello/" {
t.Error("Unexpected: ", path)
}
}

View File

@@ -10,6 +10,8 @@ import (
"sync"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
@@ -39,6 +41,10 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
return &BrowserDialerClient{}
}
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
isH3 := tlsConfig != nil && (len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3")
globalDialerAccess.Lock()
defer globalDialerAccess.Unlock()
@@ -46,17 +52,13 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
globalDialerMap = make(map[dialerConf]DialerClient)
}
if isH3 {
dest.Network = net.Network_UDP
}
if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found {
return client
}
if browser_dialer.HasBrowserDialer() {
return &BrowserDialerClient{}
}
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
var gotlsConfig *gotls.Config
if tlsConfig != nil {
@@ -83,10 +85,48 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
return conn, nil
}
var uploadTransport http.RoundTripper
var downloadTransport http.RoundTripper
var uploadTransport http.RoundTripper
if isH2 {
if isH3 {
roundTripper := &http3.RoundTripper{
TLSClientConfig: gotlsConfig,
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
if err != nil {
return nil, err
}
var udpConn *net.UDPConn
var udpAddr *net.UDPAddr
switch c := conn.(type) {
case *internet.PacketConnWrapper:
var ok bool
udpConn, ok = c.Conn.(*net.UDPConn)
if !ok {
return nil, errors.New("PacketConnWrapper does not contain a UDP connection")
}
udpAddr, err = net.ResolveUDPAddr("udp", c.Dest.String())
if err != nil {
return nil, err
}
case *net.UDPConn:
udpConn = c
udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
if err != nil {
return nil, err
}
default:
return nil, errors.New("unsupported connection type: %T", conn)
}
return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
},
}
downloadTransport = roundTripper
uploadTransport = roundTripper
} else if isH2 {
downloadTransport = &http2.Transport{
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
return dialContext(ctxInner)
@@ -107,7 +147,6 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
// http.Client and our custom dial context.
DisableKeepAlives: true,
}
// we use uploadRawPool for that
uploadTransport = nil
}
@@ -121,6 +160,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
Transport: uploadTransport,
},
isH2: isH2,
isH3: isH3,
uploadRawPool: &sync.Pool{},
dialUploadConn: dialContext,
}
@@ -141,8 +181,9 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
maxConcurrentUploads := transportConfiguration.GetNormalizedMaxConcurrentUploads()
maxUploadSize := transportConfiguration.GetNormalizedMaxUploadSize()
scMaxConcurrentPosts := transportConfiguration.GetNormalizedScMaxConcurrentPosts()
scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()
if tlsConfig != nil {
requestURL.Scheme = "https"
@@ -153,20 +194,21 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
if requestURL.Host == "" {
requestURL.Host = dest.NetAddr()
}
requestURL.Path = transportConfiguration.GetNormalizedPath()
sessionIdUuid := uuid.New()
requestURL.Path = transportConfiguration.GetNormalizedPath(sessionIdUuid.String(), true)
baseURL := requestURL.String()
httpClient := getHTTPClient(ctx, dest, streamSettings)
sessionIdUuid := uuid.New()
sessionId := sessionIdUuid.String()
baseURL := requestURL.String() + sessionId
uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize))
uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(scMaxEachPostBytes.roll()))
go func() {
requestsLimiter := semaphore.New(int(maxConcurrentUploads))
requestsLimiter := semaphore.New(int(scMaxConcurrentPosts.roll()))
var requestCounter int64
lastWrite := time.Now()
// by offloading the uploads into a buffered pipe, multiple conn.Write
// calls get automatically batched together into larger POST requests.
// without batching, bandwidth is extremely limited.
@@ -197,6 +239,14 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
}
}()
if scMinPostsIntervalMs.From > 0 {
roll := time.Duration(scMinPostsIntervalMs.roll()) * time.Millisecond
if time.Since(lastWrite) < roll {
time.Sleep(roll)
}
lastWrite = time.Now()
}
}
}()

View File

@@ -11,6 +11,8 @@ import (
"sync"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
@@ -24,9 +26,11 @@ import (
)
type requestHandler struct {
config *Config
host string
path string
ln *Listener
sessionMu *sync.Mutex
sessions sync.Map
localAddr gonet.TCPAddr
}
@@ -56,13 +60,23 @@ func (h *requestHandler) maybeReapSession(isFullyConnected *done.Instance, sessi
}
func (h *requestHandler) upsertSession(sessionId string) *httpSession {
// fast path
currentSessionAny, ok := h.sessions.Load(sessionId)
if ok {
return currentSessionAny.(*httpSession)
}
// slow path
h.sessionMu.Lock()
defer h.sessionMu.Unlock()
currentSessionAny, ok = h.sessions.Load(sessionId)
if ok {
return currentSessionAny.(*httpSession)
}
s := &httpSession{
uploadQueue: NewUploadQueue(int(2 * h.ln.config.GetNormalizedMaxConcurrentUploads())),
uploadQueue: NewUploadQueue(int(h.ln.config.GetNormalizedScMaxConcurrentPosts().To)),
isFullyConnected: done.New(),
}
@@ -109,6 +123,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
}
currentSession := h.upsertSession(sessionId)
scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To)
if request.Method == "POST" {
seq := ""
@@ -123,6 +138,13 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
}
payload, err := io.ReadAll(request.Body)
if len(payload) > scMaxEachPostBytes {
errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request had size ", len(payload), ". Adjust scMaxEachPostBytes on the server to be at least as large as client.")
writer.WriteHeader(http.StatusRequestEntityTooLarge)
return
}
if err != nil {
errors.LogInfoInner(context.Background(), err, "failed to upload")
writer.WriteHeader(http.StatusInternalServerError)
@@ -161,8 +183,10 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
// magic header instructs nginx + apache to not buffer response body
writer.Header().Set("X-Accel-Buffering", "no")
// magic header to make the HTTP middle box consider this as SSE to disable buffer
writer.Header().Set("Content-Type", "text/event-stream")
if !h.config.NoSSEHeader {
// magic header to make the HTTP middle box consider this as SSE to disable buffer
writer.Header().Set("Content-Type", "text/event-stream")
}
writer.WriteHeader(http.StatusOK)
// send a chunk immediately to enable CDN streaming.
@@ -222,10 +246,13 @@ func (c *httpResponseBodyWriter) Close() error {
type Listener struct {
sync.Mutex
server http.Server
listener net.Listener
config *Config
addConn internet.ConnHandler
server http.Server
h3server *http3.Server
listener net.Listener
h3listener *quic.EarlyListener
config *Config
addConn internet.ConnHandler
isH3 bool
}
func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
@@ -242,6 +269,17 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
var listener net.Listener
var err error
var localAddr = gonet.TCPAddr{}
handler := &requestHandler{
config: shSettings,
host: shSettings.Host,
path: shSettings.GetNormalizedPath("", false),
ln: l,
sessionMu: &sync.Mutex{},
sessions: sync.Map{},
localAddr: localAddr,
}
tlsConfig := getTLSConfig(streamSettings)
l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
if port == net.Port(0) { // unix
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
@@ -252,6 +290,29 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
return nil, errors.New("failed to listen unix domain socket(for SH) on ", address).Base(err)
}
errors.LogInfo(ctx, "listening unix domain socket(for SH) on ", address)
} else if l.isH3 { // quic
Conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
IP: address.IP(),
Port: int(port),
}, streamSettings.SocketSettings)
if err != nil {
return nil, errors.New("failed to listen UDP(for SH3) on ", address, ":", port).Base(err)
}
h3listener, err := quic.ListenEarly(Conn, tlsConfig, nil)
if err != nil {
return nil, errors.New("failed to listen QUIC(for SH3) on ", address, ":", port).Base(err)
}
l.h3listener = h3listener
errors.LogInfo(ctx, "listening QUIC(for SH3) on ", address, ":", port)
l.h3server = &http3.Server{
Handler: handler,
}
go func() {
if err := l.h3server.ServeListener(l.h3listener); err != nil {
errors.LogWarningInner(ctx, err, "failed to serve http3 for splithttp")
}
}()
} else { // tcp
localAddr = gonet.TCPAddr{
IP: address.IP(),
@@ -267,36 +328,29 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port)
}
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
listener = tls.NewListener(listener, tlsConfig)
// tcp/unix (h1/h2)
if listener != nil {
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
listener = tls.NewListener(listener, tlsConfig)
}
}
}
handler := &requestHandler{
host: shSettings.Host,
path: shSettings.GetNormalizedPath(),
ln: l,
sessions: sync.Map{},
localAddr: localAddr,
}
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
l.listener = listener
l.server = http.Server{
Handler: h2cHandler,
ReadHeaderTimeout: time.Second * 4,
MaxHeaderBytes: 8192,
}
go func() {
if err := l.server.Serve(l.listener); err != nil {
errors.LogWarningInner(ctx, err, "failed to serve http for splithttp")
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
l.listener = listener
l.server = http.Server{
Handler: h2cHandler,
ReadHeaderTimeout: time.Second * 4,
MaxHeaderBytes: 8192,
}
}()
go func() {
if err := l.server.Serve(l.listener); err != nil {
errors.LogWarningInner(ctx, err, "failed to serve http for splithttp")
}
}()
}
return l, err
}
@@ -308,9 +362,22 @@ func (ln *Listener) Addr() net.Addr {
// Close implements net.Listener.Close().
func (ln *Listener) Close() error {
return ln.listener.Close()
if ln.h3server != nil {
if err := ln.h3server.Close(); err != nil {
return err
}
} else if ln.listener != nil {
return ln.listener.Close()
}
return errors.New("listener does not have an HTTP/3 server or a net.listener")
}
func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *tls.Config {
config := v2tls.ConfigFromStreamSettings(streamSettings)
if config == nil {
return &tls.Config{}
}
return config.GetTLSConfig()
}
func init() {
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
}

View File

@@ -2,6 +2,7 @@ package splithttp_test
import (
"context"
"crypto/rand"
gotls "crypto/tls"
"fmt"
gonet "net"
@@ -10,10 +11,13 @@ import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/tls/cert"
"github.com/xtls/xray-core/testing/servers/tcp"
"github.com/xtls/xray-core/testing/servers/udp"
"github.com/xtls/xray-core/transport/internet"
. "github.com/xtls/xray-core/transport/internet/splithttp"
"github.com/xtls/xray-core/transport/internet/stat"
@@ -63,8 +67,8 @@ func Test_listenSHAndDial(t *testing.T) {
}
common.Must(conn.Close())
<-time.After(time.Second * 5)
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 2"))
common.Must(err)
@@ -96,7 +100,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
return
}
_, err = c.Write([]byte("Response"))
_, err = c.Write([]byte(c.RemoteAddr().String()))
common.Must(err)
}(conn)
})
@@ -113,7 +117,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
var b [1024]byte
n, _ := conn.Read(b[:])
if string(b[:n]) != "Response" {
if string(b[:n]) != "1.1.1.1:0" {
t.Error("response: ", string(b[:n]))
}
@@ -142,7 +146,16 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
}
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
go func() {
_ = conn.Close()
defer conn.Close()
var b [1024]byte
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
_, err := conn.Read(b[:])
if err != nil {
return
}
common.Must2(conn.Write([]byte("Response")))
}()
})
common.Must(err)
@@ -150,7 +163,15 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
_ = conn.Close()
_, err = conn.Write([]byte("Test connection 1"))
common.Must(err)
var b [1024]byte
n, _ := conn.Read(b[:])
if string(b[:n]) != "Response" {
t.Error("response: ", string(b[:n]))
}
end := time.Now()
if !end.Before(start.Add(time.Second * 5)) {
@@ -204,3 +225,184 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
t.Error("Expected h2 but got:", resp.ProtoMajor)
}
}
func Test_listenSHAndDial_QUIC(t *testing.T) {
if runtime.GOARCH == "arm64" {
return
}
listenPort := udp.PickPort()
start := time.Now()
streamSettings := &internet.MemoryStreamConfig{
ProtocolName: "splithttp",
ProtocolSettings: &Config{
Path: "shs",
},
SecurityType: "tls",
SecuritySettings: &tls.Config{
AllowInsecure: true,
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
NextProtocol: []string{"h3"},
},
}
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
go func() {
defer conn.Close()
b := buf.New()
defer b.Release()
for {
b.Clear()
if _, err := b.ReadFrom(conn); err != nil {
return
}
common.Must2(conn.Write(b.Bytes()))
}
}()
})
common.Must(err)
defer listen.Close()
time.Sleep(time.Second)
conn, err := Dial(context.Background(), net.UDPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
defer conn.Close()
const N = 1024
b1 := make([]byte, N)
common.Must2(rand.Read(b1))
b2 := buf.New()
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
end := time.Now()
if !end.Before(start.Add(time.Second * 5)) {
t.Error("end: ", end, " start: ", start)
}
}
func Test_listenSHAndDial_Unix(t *testing.T) {
tempDir := t.TempDir()
tempSocket := tempDir + "/server.sock"
listen, err := ListenSH(context.Background(), net.DomainAddress(tempSocket), 0, &internet.MemoryStreamConfig{
ProtocolName: "splithttp",
ProtocolSettings: &Config{
Path: "/sh",
},
}, func(conn stat.Connection) {
go func(c stat.Connection) {
defer c.Close()
var b [1024]byte
c.SetReadDeadline(time.Now().Add(2 * time.Second))
_, err := c.Read(b[:])
if err != nil {
return
}
common.Must2(c.Write([]byte("Response")))
}(conn)
})
common.Must(err)
ctx := context.Background()
streamSettings := &internet.MemoryStreamConfig{
ProtocolName: "splithttp",
ProtocolSettings: &Config{
Host: "example.com",
Path: "sh",
},
}
conn, err := Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 1"))
common.Must(err)
var b [1024]byte
fmt.Println("test2")
n, _ := conn.Read(b[:])
fmt.Println("string is", n)
if string(b[:n]) != "Response" {
t.Error("response: ", string(b[:n]))
}
common.Must(conn.Close())
conn, err = Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 2"))
common.Must(err)
n, _ = conn.Read(b[:])
common.Must(err)
if string(b[:n]) != "Response" {
t.Error("response: ", string(b[:n]))
}
common.Must(conn.Close())
common.Must(listen.Close())
}
func Test_queryString(t *testing.T) {
listenPort := tcp.PickPort()
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
ProtocolName: "splithttp",
ProtocolSettings: &Config{
// this querystring does not have any effect, but sometimes people blindly copy it from websocket config. make sure the outbound doesn't break
Path: "/sh?ed=2048",
},
}, func(conn stat.Connection) {
go func(c stat.Connection) {
defer c.Close()
var b [1024]byte
c.SetReadDeadline(time.Now().Add(2 * time.Second))
_, err := c.Read(b[:])
if err != nil {
return
}
common.Must2(c.Write([]byte("Response")))
}(conn)
})
common.Must(err)
ctx := context.Background()
streamSettings := &internet.MemoryStreamConfig{
ProtocolName: "splithttp",
ProtocolSettings: &Config{Path: "sh"},
}
conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 1"))
common.Must(err)
var b [1024]byte
fmt.Println("test2")
n, _ := conn.Read(b[:])
fmt.Println("string is", n)
if string(b[:n]) != "Response" {
t.Error("response: ", string(b[:n]))
}
common.Must(conn.Close())
common.Must(listen.Close())
}

View File

@@ -6,6 +6,7 @@ package splithttp
import (
"container/heap"
"io"
"sync"
"github.com/xtls/xray-core/common/errors"
)
@@ -16,11 +17,12 @@ type Packet struct {
}
type uploadQueue struct {
pushedPackets chan Packet
heap uploadHeap
nextSeq uint64
closed bool
maxPackets int
pushedPackets chan Packet
writeCloseMutex sync.Mutex
heap uploadHeap
nextSeq uint64
closed bool
maxPackets int
}
func NewUploadQueue(maxPackets int) *uploadQueue {
@@ -34,6 +36,9 @@ func NewUploadQueue(maxPackets int) *uploadQueue {
}
func (h *uploadQueue) Push(p Packet) error {
h.writeCloseMutex.Lock()
defer h.writeCloseMutex.Unlock()
if h.closed {
return errors.New("splithttp packet queue closed")
}
@@ -43,6 +48,9 @@ func (h *uploadQueue) Push(p Packet) error {
}
func (h *uploadQueue) Close() error {
h.writeCloseMutex.Lock()
defer h.writeCloseMutex.Unlock()
h.closed = true
close(h.pushedPackets)
return nil

View File

@@ -10,6 +10,7 @@ import (
"strings"
"sync"
"time"
"bytes"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
@@ -50,72 +51,84 @@ func (c *Config) BuildCertificates() []*tls.Certificate {
if entry.Usage != Certificate_ENCIPHERMENT {
continue
}
keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
if err != nil {
errors.LogWarningInner(context.Background(), err, "ignoring invalid X509 key pair")
continue
}
keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
if err != nil {
errors.LogWarningInner(context.Background(), err, "ignoring invalid certificate")
continue
}
certs = append(certs, &keyPair)
if !entry.OneTimeLoading {
var isOcspstapling bool
hotReloadCertInterval := uint64(3600)
if entry.OcspStapling != 0 {
hotReloadCertInterval = entry.OcspStapling
isOcspstapling = true
getX509KeyPair := func() *tls.Certificate {
keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
if err != nil {
errors.LogWarningInner(context.Background(), err, "ignoring invalid X509 key pair")
return nil
}
index := len(certs) - 1
go func(entry *Certificate, cert *tls.Certificate, index int) {
t := time.NewTicker(time.Duration(hotReloadCertInterval) * time.Second)
for {
if entry.CertificatePath != "" && entry.KeyPath != "" {
newCert, err := filesystem.ReadFile(entry.CertificatePath)
if err != nil {
errors.LogErrorInner(context.Background(), err, "failed to parse certificate")
<-t.C
continue
}
newKey, err := filesystem.ReadFile(entry.KeyPath)
if err != nil {
errors.LogErrorInner(context.Background(), err, "failed to parse key")
<-t.C
continue
}
if string(newCert) != string(entry.Certificate) && string(newKey) != string(entry.Key) {
newKeyPair, err := tls.X509KeyPair(newCert, newKey)
if err != nil {
errors.LogErrorInner(context.Background(), err, "ignoring invalid X509 key pair")
<-t.C
continue
}
if newKeyPair.Leaf, err = x509.ParseCertificate(newKeyPair.Certificate[0]); err != nil {
errors.LogErrorInner(context.Background(), err, "ignoring invalid certificate")
<-t.C
continue
}
cert = &newKeyPair
}
}
if isOcspstapling {
if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
errors.LogWarningInner(context.Background(), err, "ignoring invalid OCSP")
} else if string(newOCSPData) != string(cert.OCSPStaple) {
cert.OCSPStaple = newOCSPData
}
}
certs[index] = cert
<-t.C
}
}(entry, certs[index], index)
keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
if err != nil {
errors.LogWarningInner(context.Background(), err, "ignoring invalid certificate")
return nil
}
return &keyPair
}
if keyPair := getX509KeyPair(); keyPair != nil {
certs = append(certs, keyPair)
} else {
continue
}
index := len(certs) - 1
setupOcspTicker(entry, func(isReloaded, isOcspstapling bool){
cert := certs[index]
if isReloaded {
if newKeyPair := getX509KeyPair(); newKeyPair != nil {
cert = newKeyPair
} else {
return
}
}
if isOcspstapling {
if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
errors.LogWarningInner(context.Background(), err, "ignoring invalid OCSP")
} else if string(newOCSPData) != string(cert.OCSPStaple) {
cert.OCSPStaple = newOCSPData
}
}
certs[index] = cert
})
}
return certs
}
func setupOcspTicker(entry *Certificate, callback func(isReloaded, isOcspstapling bool)) {
go func() {
if entry.OneTimeLoading {
return
}
var isOcspstapling bool
hotReloadCertInterval := uint64(3600)
if entry.OcspStapling != 0 {
hotReloadCertInterval = entry.OcspStapling
isOcspstapling = true
}
t := time.NewTicker(time.Duration(hotReloadCertInterval) * time.Second)
for {
var isReloaded bool
if entry.CertificatePath != "" && entry.KeyPath != "" {
newCert, err := filesystem.ReadFile(entry.CertificatePath)
if err != nil {
errors.LogErrorInner(context.Background(), err, "failed to parse certificate")
return
}
newKey, err := filesystem.ReadFile(entry.KeyPath)
if err != nil {
errors.LogErrorInner(context.Background(), err, "failed to parse key")
return
}
if string(newCert) != string(entry.Certificate) || string(newKey) != string(entry.Key) {
entry.Certificate = newCert
entry.Key = newKey
isReloaded = true
}
}
callback(isReloaded, isOcspstapling)
<-t.C
}
}()
}
func isCertificateExpired(c *tls.Certificate) bool {
if c.Leaf == nil && len(c.Certificate) > 0 {
if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
@@ -137,6 +150,9 @@ func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, erro
return nil, errors.New("failed to generate new certificate for ", domain).Base(err)
}
newCertPEM, newKeyPEM := newCert.ToPEM()
if rawCA.BuildChain {
newCertPEM = bytes.Join([][]byte{newCertPEM, rawCA.Certificate}, []byte("\n"))
}
cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
return &cert, err
}
@@ -146,6 +162,7 @@ func (c *Config) getCustomCA() []*Certificate {
for _, certificate := range c.Certificate {
if certificate.Usage == Certificate_AUTHORITY_ISSUE {
certs = append(certs, certificate)
setupOcspTicker(certificate, func(isReloaded, isOcspstapling bool){ })
}
}
return certs

View File

@@ -86,6 +86,7 @@ type Certificate struct {
KeyPath string `protobuf:"bytes,6,opt,name=key_path,json=keyPath,proto3" json:"key_path,omitempty"`
// If true, one-Time Loading
OneTimeLoading bool `protobuf:"varint,7,opt,name=One_time_loading,json=OneTimeLoading,proto3" json:"One_time_loading,omitempty"`
BuildChain bool `protobuf:"varint,8,opt,name=build_chain,json=buildChain,proto3" json:"build_chain,omitempty"`
}
func (x *Certificate) Reset() {
@@ -169,6 +170,13 @@ func (x *Certificate) GetOneTimeLoading() bool {
return false
}
func (x *Certificate) GetBuildChain() bool {
if x != nil {
return x.BuildChain
}
return false
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -359,7 +367,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
0x6c, 0x73, 0x22, 0xe2, 0x02, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x6c, 0x73, 0x22, 0x83, 0x03, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
@@ -377,66 +385,68 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e,
0x67, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x4e,
0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10,
0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59,
0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f,
0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xf6, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65,
0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f,
0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, 0x72,
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72,
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6e,
0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19, 0x65,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65,
0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17,
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62,
0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06,
0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79, 0x73,
0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x69,
0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69, 0x70,
0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x41,
0x0a, 0x1b, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65,
0x73, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e,
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52,
0x10, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e,
0x69, 0x12, 0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72,
0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61,
0x69, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52,
0x20, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35,
0x36, 0x12, 0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72,
0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e,
0x20, 0x03, 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72,
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61,
0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67,
0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e,
0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x68, 0x61,
0x69, 0x6e, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45,
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xf6, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c,
0x6e, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19,
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72,
0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
0x17, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61,
0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18,
0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79,
0x73, 0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78,
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69,
0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12,
0x41, 0x0a, 0x1b, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x5f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74,
0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
0x72, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75,
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08,
0x52, 0x10, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53,
0x6e, 0x69, 0x12, 0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65,
0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68,
0x61, 0x69, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c,
0x52, 0x20, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32,
0x35, 0x36, 0x12, 0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65,
0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75,
0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18,
0x0e, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65,
0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d,
0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f,
0x67, 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 (

View File

@@ -31,6 +31,8 @@ message Certificate {
// If true, one-Time Loading
bool One_time_loading = 7;
bool build_chain = 8;
}
message Config {

View File

@@ -14,15 +14,19 @@ import (
var _ buf.Writer = (*connection)(nil)
// connection is a wrapper for net.Conn over WebSocket connection.
// remoteAddr is used to pass "virtual" remote IP addresses in X-Forwarded-For.
// so we shouldn't directly read it form conn.
type connection struct {
conn *websocket.Conn
reader io.Reader
conn *websocket.Conn
reader io.Reader
remoteAddr net.Addr
}
func NewConnection(conn *websocket.Conn, remoteAddr net.Addr, extraReader io.Reader) *connection {
return &connection{
conn: conn,
reader: extraReader,
conn: conn,
remoteAddr: remoteAddr,
reader: extraReader,
}
}
@@ -90,7 +94,7 @@ func (c *connection) LocalAddr() net.Addr {
}
func (c *connection) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
return c.remoteAddr
}
func (c *connection) SetDeadline(t time.Time) error {

View File

@@ -58,7 +58,6 @@ func Test_listenWSAndDial(t *testing.T) {
}
common.Must(conn.Close())
<-time.After(time.Second * 5)
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 2"))
@@ -91,7 +90,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
return
}
_, err = c.Write([]byte("Response"))
_, err = c.Write([]byte(c.RemoteAddr().String()))
common.Must(err)
}(conn)
})
@@ -109,7 +108,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
var b [1024]byte
n, err := conn.Read(b[:])
common.Must(err)
if string(b[:n]) != "Response" {
if string(b[:n]) != "1.1.1.1:0" {
t.Error("response: ", string(b[:n]))
}