mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 09:36:49 +08:00
Compare commits
25 Commits
fallback-h
...
v1.8.20
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8deb953aec | ||
![]() |
a0040f13dd | ||
![]() |
d8994b7603 | ||
![]() |
b277bacdf6 | ||
![]() |
9288a7c0dc | ||
![]() |
c40fc44a34 | ||
![]() |
02cd3b8c74 | ||
![]() |
a7e198e1e2 | ||
![]() |
9e6d7a3cb0 | ||
![]() |
a4bc422ed1 | ||
![]() |
59819e2a1b | ||
![]() |
573fb4f643 | ||
![]() |
558cfcc507 | ||
![]() |
39675b7ef7 | ||
![]() |
16de0937a8 | ||
![]() |
c69d38ae82 | ||
![]() |
73a001dd7a | ||
![]() |
c8f6ba9ff0 | ||
![]() |
308f0c64c3 | ||
![]() |
ce637c0c23 | ||
![]() |
0d130a0489 | ||
![]() |
01a3b4912b | ||
![]() |
b8c0768b16 | ||
![]() |
4c51636788 | ||
![]() |
1113ee7fa2 |
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
contact_links:
|
||||||
|
- name: Community Support and Questions
|
||||||
|
url: https://github.com/XTLS/Xray-core/discussions
|
||||||
|
about: Please ask and answer questions there. The issue tracker is for issues with core.
|
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
@@ -78,12 +78,15 @@ jobs:
|
|||||||
# Include amd64 on all platforms.
|
# Include amd64 on all platforms.
|
||||||
goos: [windows, freebsd, openbsd, linux, darwin]
|
goos: [windows, freebsd, openbsd, linux, darwin]
|
||||||
goarch: [amd64, 386]
|
goarch: [amd64, 386]
|
||||||
|
gotoolchain: [""]
|
||||||
|
patch-assetname: [""]
|
||||||
|
|
||||||
exclude:
|
exclude:
|
||||||
# Exclude i386 on darwin
|
# Exclude i386 on darwin
|
||||||
- goarch: 386
|
- goarch: 386
|
||||||
goos: darwin
|
goos: darwin
|
||||||
include:
|
include:
|
||||||
# BEIGIN MacOS ARM64
|
# BEGIN MacOS ARM64
|
||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
# END MacOS ARM64
|
# END MacOS ARM64
|
||||||
@@ -152,6 +155,16 @@ jobs:
|
|||||||
goarch: arm
|
goarch: arm
|
||||||
goarm: 7
|
goarm: 7
|
||||||
# END OPENBSD ARM
|
# 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
|
fail-fast: false
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -164,16 +177,17 @@ jobs:
|
|||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Show workflow information
|
- name: Show workflow information
|
||||||
run: |
|
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 "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ${{ matrix.gotoolchain || '1.22' }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Get project dependencies
|
- name: Get project dependencies
|
||||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: '1.22'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- name: Restore Cache
|
- name: Restore Cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
|
4
Makefile
4
Makefile
@@ -3,13 +3,13 @@ NAME = xray
|
|||||||
VERSION=$(shell git describe --always --dirty)
|
VERSION=$(shell git describe --always --dirty)
|
||||||
|
|
||||||
# NOTE: This MAKEFILE can be used to build Xray-core locally and in Automatic workflows. It is \
|
# NOTE: This MAKEFILE can be used to build Xray-core locally and in Automatic workflows. It is \
|
||||||
provided for convinience in automatic building and functions as a part of it.
|
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:\
|
# 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 \
|
- This file is not the main Makefile; it only accepts environment variables and builds the \
|
||||||
binary.\
|
binary.\
|
||||||
- Automatic building expects the correct binaries to be built by this Makefile. If you \
|
- 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 \
|
intend to propose a change to this Makefile, carefully review the file below and ensure \
|
||||||
that the change will not accidently break the automatic building:\
|
that the change will not accidentally break the automatic building:\
|
||||||
.github/workflows/release.yml \
|
.github/workflows/release.yml \
|
||||||
Otherwise it is recommended to contact the project maintainers.
|
Otherwise it is recommended to contact the project maintainers.
|
||||||
|
|
||||||
|
13
README.md
13
README.md
@@ -27,8 +27,7 @@
|
|||||||
- [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**)
|
- [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**)
|
||||||
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
|
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
|
||||||
- Web Panel
|
- 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)
|
- [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [Xray-UI](https://github.com/qist/xray-ui)
|
||||||
- [Xray-UI](https://github.com/qist/xray-ui), [X-UI](https://github.com/sing-web/x-ui)
|
|
||||||
- [Hiddify](https://github.com/hiddify/hiddify-config)
|
- [Hiddify](https://github.com/hiddify/hiddify-config)
|
||||||
- [Marzban](https://github.com/Gozargah/Marzban)
|
- [Marzban](https://github.com/Gozargah/Marzban)
|
||||||
- [Libertea](https://github.com/VZiChoushaDui/Libertea)
|
- [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))
|
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
||||||
- Windows
|
- Windows
|
||||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||||
- [NekoRay](https://github.com/Matsuridayo/nekoray)
|
|
||||||
- [Furious](https://github.com/LorenEteval/Furious)
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
- [HiddifyN](https://github.com/hiddify/HiddifyN)
|
|
||||||
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
|
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
|
||||||
- Android
|
- Android
|
||||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||||
- [HiddifyNG](https://github.com/hiddify/HiddifyNG)
|
|
||||||
- [X-flutter](https://github.com/XTLS/X-flutter)
|
- [X-flutter](https://github.com/XTLS/X-flutter)
|
||||||
- iOS & macOS arm64
|
- iOS & macOS arm64
|
||||||
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
||||||
@@ -85,7 +81,6 @@
|
|||||||
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
||||||
- Linux
|
- Linux
|
||||||
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
||||||
- [NekoRay](https://github.com/Matsuridayo/nekoray)
|
|
||||||
- [Furious](https://github.com/LorenEteval/Furious)
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
|
|
||||||
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
||||||
@@ -98,21 +93,15 @@
|
|||||||
- [XTLS/libXray](https://github.com/XTLS/libXray)
|
- [XTLS/libXray](https://github.com/XTLS/libXray)
|
||||||
- [xtlsapi](https://github.com/hiddify/xtlsapi)
|
- [xtlsapi](https://github.com/hiddify/xtlsapi)
|
||||||
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
|
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
|
||||||
- [XrayKit](https://github.com/arror/XrayKit)
|
|
||||||
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
|
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
|
||||||
- [xray-api](https://github.com/XVGuardian/xray-api)
|
- [xray-api](https://github.com/XVGuardian/xray-api)
|
||||||
- [XrayR](https://github.com/XrayR-project/XrayR)
|
- [XrayR](https://github.com/XrayR-project/XrayR)
|
||||||
- [XrayR-release](https://github.com/XrayR-project/XrayR-release)
|
- [XrayR-release](https://github.com/XrayR-project/XrayR-release)
|
||||||
- [XrayR-V2Board](https://github.com/missuo/XrayR-V2Board)
|
- [XrayR-V2Board](https://github.com/missuo/XrayR-V2Board)
|
||||||
- [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta)
|
- [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta)
|
||||||
- [Clash Verge](https://github.com/zzzgydi/clash-verge)
|
|
||||||
- [clashN](https://github.com/2dust/clashN)
|
- [clashN](https://github.com/2dust/clashN)
|
||||||
- [Clash Meta for Android](https://github.com/MetaCubeX/ClashMetaForAndroid)
|
- [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)
|
- [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
|
## Contributing
|
||||||
|
|
||||||
|
@@ -85,7 +85,7 @@ func NewClient(
|
|||||||
return errors.New("failed to create nameserver").Base(err).AtWarning()
|
return errors.New("failed to create nameserver").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priotize local domains with specific TLDs or without any dot to local DNS
|
// Prioritize local domains with specific TLDs or those without any dot for the local DNS
|
||||||
if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
|
if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
|
||||||
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
|
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
|
||||||
ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
|
ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
|
||||||
|
@@ -259,7 +259,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
|
|||||||
})
|
})
|
||||||
|
|
||||||
// forced to use mux for DOH
|
// forced to use mux for DOH
|
||||||
// dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true)
|
// dnsCtx = session.ContextWithMuxPreferred(dnsCtx, true)
|
||||||
|
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
|
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
|
||||||
|
@@ -30,8 +30,8 @@ func New(ctx context.Context, config *Config) (*Instance, error) {
|
|||||||
}
|
}
|
||||||
log.RegisterHandler(g)
|
log.RegisterHandler(g)
|
||||||
|
|
||||||
// start logger instantly on inited
|
// Start logger instantly on initialization
|
||||||
// other modules would log during init
|
// Other modules would log during initialization
|
||||||
if err := g.startInternal(); err != nil {
|
if err := g.startInternal(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -209,7 +209,7 @@ func (h *HealthPing) PutResult(tag string, rtt time.Duration) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
// validity is 2 times to sampling period, since the check are
|
// validity is 2 times to sampling period, since the check are
|
||||||
// distributed in the time line randomly, in extreme cases,
|
// distributed in the time line randomly, in extreme cases,
|
||||||
// previous checks are distributed on the left, and latters
|
// Previous checks are distributed on the left, and later ones
|
||||||
// on the right
|
// on the right
|
||||||
validity := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) * 2
|
validity := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) * 2
|
||||||
r = NewHealthPingResult(h.Settings.SamplingCount, validity)
|
r = NewHealthPingResult(h.Settings.SamplingCount, validity)
|
||||||
|
@@ -272,7 +272,7 @@ func TestServiceSubscribeSubsetOfFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSerivceTestRoute(t *testing.T) {
|
func TestServiceTestRoute(t *testing.T) {
|
||||||
c := stats.NewChannel(&stats.ChannelConfig{
|
c := stats.NewChannel(&stats.ChannelConfig{
|
||||||
SubscriberLimit: 1,
|
SubscriberLimit: 1,
|
||||||
BufferSize: 16,
|
BufferSize: 16,
|
||||||
|
@@ -95,7 +95,7 @@ func TestStatsChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStatsChannelUnsubcribe(t *testing.T) {
|
func TestStatsChannelUnsubscribe(t *testing.T) {
|
||||||
c := NewChannel(&ChannelConfig{Blocking: true})
|
c := NewChannel(&ChannelConfig{Blocking: true})
|
||||||
common.Must(c.Start())
|
common.Must(c.Start())
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
@@ -18,7 +18,7 @@ func TestStatsCounter(t *testing.T) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
if v := c.Add(1); v != 1 {
|
if v := c.Add(1); v != 1 {
|
||||||
t.Fatal("unpexcted Add(1) return: ", v, ", wanted ", 1)
|
t.Fatal("unexpected Add(1) return: ", v, ", wanted ", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := c.Set(0); v != 1 {
|
if v := c.Set(0); v != 1 {
|
||||||
|
@@ -106,7 +106,7 @@ func TestMultiBufferReadAllToByte(t *testing.T) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
if l := len(b); l != 8*1024 {
|
if l := len(b); l != 8*1024 {
|
||||||
t.Error("unexpceted length from ReadAllToBytes", l)
|
t.Error("unexpected length from ReadAllToBytes", l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -139,7 +139,7 @@ func TestMultiBufferCopy(t *testing.T) {
|
|||||||
mb.Copy(lbdst)
|
mb.Copy(lbdst)
|
||||||
|
|
||||||
if d := cmp.Diff(lb, lbdst); d != "" {
|
if d := cmp.Diff(lb, lbdst); d != "" {
|
||||||
t.Error("unexpceted different from MultiBufferCopy ", d)
|
t.Error("unexpected different from MultiBufferCopy ", d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -41,8 +41,8 @@ type BufferedReader struct {
|
|||||||
Reader Reader
|
Reader Reader
|
||||||
// Buffer is the internal buffer to be read from first
|
// Buffer is the internal buffer to be read from first
|
||||||
Buffer MultiBuffer
|
Buffer MultiBuffer
|
||||||
// Spliter is a function to read bytes from MultiBuffer
|
// Splitter is a function to read bytes from MultiBuffer
|
||||||
Spliter func(MultiBuffer, []byte) (MultiBuffer, int)
|
Splitter func(MultiBuffer, []byte) (MultiBuffer, int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BufferedBytes returns the number of bytes that is cached in this reader.
|
// BufferedBytes returns the number of bytes that is cached in this reader.
|
||||||
@@ -59,7 +59,7 @@ func (r *BufferedReader) ReadByte() (byte, error) {
|
|||||||
|
|
||||||
// Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader.
|
// Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader.
|
||||||
func (r *BufferedReader) Read(b []byte) (int, error) {
|
func (r *BufferedReader) Read(b []byte) (int, error) {
|
||||||
spliter := r.Spliter
|
spliter := r.Splitter
|
||||||
if spliter == nil {
|
if spliter == nil {
|
||||||
spliter = SplitBytes
|
spliter = SplitBytes
|
||||||
}
|
}
|
||||||
|
@@ -151,7 +151,7 @@ func LogInfo(ctx context.Context, msg ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func LogInfoInner(ctx context.Context, inner error, 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{}) {
|
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{}) {
|
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{}) {
|
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{}) {
|
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{}) {
|
func doLog(ctx context.Context, inner error, severity log.Severity, msg ...interface{}) {
|
||||||
|
@@ -51,8 +51,8 @@ func ConnectionOutputMulti(reader buf.Reader) ConnectionOption {
|
|||||||
func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption {
|
func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption {
|
||||||
return func(c *connection) {
|
return func(c *connection) {
|
||||||
c.reader = &buf.BufferedReader{
|
c.reader = &buf.BufferedReader{
|
||||||
Reader: reader,
|
Reader: reader,
|
||||||
Spliter: buf.SplitFirstBytes,
|
Splitter: buf.SplitFirstBytes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@ func GetToolLocation(file string) string {
|
|||||||
return filepath.Join(toolPath, file+".exe")
|
return filepath.Join(toolPath, file+".exe")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAssetLocation searches for `file` in the excutable dir
|
// GetAssetLocation searches for `file` in the executable dir
|
||||||
func GetAssetLocation(file string) string {
|
func GetAssetLocation(file string) string {
|
||||||
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
|
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
|
||||||
return filepath.Join(assetPath, file)
|
return filepath.Join(assetPath, file)
|
||||||
|
@@ -19,7 +19,7 @@ import (
|
|||||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||||
|
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
// Cerificate in ASN.1 DER format
|
// certificate in ASN.1 DER format
|
||||||
Certificate []byte
|
Certificate []byte
|
||||||
// Private key in ASN.1 DER format
|
// Private key in ASN.1 DER format
|
||||||
PrivateKey []byte
|
PrivateKey []byte
|
||||||
|
@@ -147,7 +147,7 @@ func TestTLSHeaders(t *testing.T) {
|
|||||||
header, err := SniffTLS(test.input)
|
header, err := SniffTLS(test.input)
|
||||||
if test.err {
|
if test.err {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Exepct error but nil in test %v", test)
|
t.Errorf("Expect error but nil in test %v", test)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -21,7 +21,7 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
// User is a generic user for all procotols.
|
// User is a generic user for all protocols.
|
||||||
type User struct {
|
type User struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@@ -8,7 +8,7 @@ option java_multiple_files = true;
|
|||||||
|
|
||||||
import "common/serial/typed_message.proto";
|
import "common/serial/typed_message.proto";
|
||||||
|
|
||||||
// User is a generic user for all procotols.
|
// User is a generic user for all protocols.
|
||||||
message User {
|
message User {
|
||||||
uint32 level = 1;
|
uint32 level = 1;
|
||||||
string email = 2;
|
string email = 2;
|
||||||
|
@@ -13,16 +13,16 @@ import (
|
|||||||
func IndependentCancelCtx(parent context.Context) context.Context
|
func IndependentCancelCtx(parent context.Context) context.Context
|
||||||
|
|
||||||
const (
|
const (
|
||||||
inboundSessionKey ctx.SessionKey = 1
|
inboundSessionKey ctx.SessionKey = 1
|
||||||
outboundSessionKey ctx.SessionKey = 2
|
outboundSessionKey ctx.SessionKey = 2
|
||||||
contentSessionKey ctx.SessionKey = 3
|
contentSessionKey ctx.SessionKey = 3
|
||||||
muxPreferedSessionKey ctx.SessionKey = 4
|
muxPreferredSessionKey ctx.SessionKey = 4
|
||||||
sockoptSessionKey ctx.SessionKey = 5
|
sockoptSessionKey ctx.SessionKey = 5
|
||||||
trackedConnectionErrorKey ctx.SessionKey = 6
|
trackedConnectionErrorKey ctx.SessionKey = 6
|
||||||
dispatcherKey ctx.SessionKey = 7
|
dispatcherKey ctx.SessionKey = 7
|
||||||
timeoutOnlyKey ctx.SessionKey = 8
|
timeoutOnlyKey ctx.SessionKey = 8
|
||||||
allowedNetworkKey ctx.SessionKey = 9
|
allowedNetworkKey ctx.SessionKey = 9
|
||||||
handlerSessionKey ctx.SessionKey = 10
|
handlerSessionKey ctx.SessionKey = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
|
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
|
||||||
@@ -58,14 +58,14 @@ func ContentFromContext(ctx context.Context) *Content {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContextWithMuxPrefered returns a new context with the given bool
|
// ContextWithMuxPreferred returns a new context with the given bool
|
||||||
func ContextWithMuxPrefered(ctx context.Context, forced bool) context.Context {
|
func ContextWithMuxPreferred(ctx context.Context, forced bool) context.Context {
|
||||||
return context.WithValue(ctx, muxPreferedSessionKey, forced)
|
return context.WithValue(ctx, muxPreferredSessionKey, forced)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MuxPreferedFromContext returns value in this context, or false if not contained.
|
// MuxPreferredFromContext returns value in this context, or false if not contained.
|
||||||
func MuxPreferedFromContext(ctx context.Context) bool {
|
func MuxPreferredFromContext(ctx context.Context) bool {
|
||||||
if val, ok := ctx.Value(muxPreferedSessionKey).(bool); ok {
|
if val, ok := ctx.Value(muxPreferredSessionKey).(bool); ok {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@@ -29,7 +29,7 @@ func TestActivityTimerUpdate(t *testing.T) {
|
|||||||
timer.SetTimeout(time.Second * 1)
|
timer.SetTimeout(time.Second * 1)
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
if ctx.Err() == nil {
|
if ctx.Err() == nil {
|
||||||
t.Error("expcted some error, but got nil")
|
t.Error("expected some error, but got nil")
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(timer)
|
runtime.KeepAlive(timer)
|
||||||
}
|
}
|
||||||
|
@@ -174,7 +174,7 @@ func init() {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
return loadProtobufConfig(data)
|
return loadProtobufConfig(data)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknow type")
|
return nil, errors.New("unknown type")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@@ -21,7 +21,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
Version_x byte = 1
|
Version_x byte = 1
|
||||||
Version_y byte = 8
|
Version_y byte = 8
|
||||||
Version_z byte = 16
|
Version_z byte = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
17
go.mod
17
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/xtls/xray-core
|
module github.com/xtls/xray-core
|
||||||
|
|
||||||
go 1.22
|
go 1.21.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
|
||||||
@@ -13,19 +13,19 @@ require (
|
|||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
github.com/pires/go-proxyproto v0.7.0
|
github.com/pires/go-proxyproto v0.7.0
|
||||||
github.com/quic-go/quic-go v0.45.1
|
github.com/quic-go/quic-go v0.45.1
|
||||||
github.com/refraction-networking/utls v1.6.6
|
github.com/refraction-networking/utls v1.6.7
|
||||||
github.com/sagernet/sing v0.4.1
|
github.com/sagernet/sing v0.4.1
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
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/stretchr/testify v1.9.0
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3
|
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3
|
||||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.24.0
|
golang.org/x/crypto v0.25.0
|
||||||
golang.org/x/net v0.26.0
|
golang.org/x/net v0.27.0
|
||||||
golang.org/x/sync v0.7.0
|
golang.org/x/sync v0.7.0
|
||||||
golang.org/x/sys v0.21.0
|
golang.org/x/sys v0.22.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||||
google.golang.org/grpc v1.65.0
|
google.golang.org/grpc v1.65.0
|
||||||
google.golang.org/protobuf v1.34.2
|
google.golang.org/protobuf v1.34.2
|
||||||
@@ -46,6 +46,7 @@ require (
|
|||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.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/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
|
30
go.sum
30
go.sum
@@ -110,19 +110,21 @@ 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/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/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/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 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA=
|
||||||
github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
||||||
github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig=
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
|
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 v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
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-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
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/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/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=
|
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||||
@@ -163,8 +165,8 @@ github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mo
|
|||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc h1:0Nj8T1n7F7+v4vRVroaJIvY6R0vNABLfPH+lzPHRJvI=
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg=
|
||||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
@@ -177,8 +179,8 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
@@ -199,8 +201,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -226,8 +228,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@@ -685,23 +685,31 @@ func (p TransportProtocol) Build() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomSockoptConfig struct {
|
||||||
|
Level string `json:"level"`
|
||||||
|
Opt string `json:"opt"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
type SocketConfig struct {
|
type SocketConfig struct {
|
||||||
Mark int32 `json:"mark"`
|
Mark int32 `json:"mark"`
|
||||||
TFO interface{} `json:"tcpFastOpen"`
|
TFO interface{} `json:"tcpFastOpen"`
|
||||||
TProxy string `json:"tproxy"`
|
TProxy string `json:"tproxy"`
|
||||||
AcceptProxyProtocol bool `json:"acceptProxyProtocol"`
|
AcceptProxyProtocol bool `json:"acceptProxyProtocol"`
|
||||||
DomainStrategy string `json:"domainStrategy"`
|
DomainStrategy string `json:"domainStrategy"`
|
||||||
DialerProxy string `json:"dialerProxy"`
|
DialerProxy string `json:"dialerProxy"`
|
||||||
TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"`
|
TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"`
|
||||||
TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"`
|
TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"`
|
||||||
TCPCongestion string `json:"tcpCongestion"`
|
TCPCongestion string `json:"tcpCongestion"`
|
||||||
TCPWindowClamp int32 `json:"tcpWindowClamp"`
|
TCPWindowClamp int32 `json:"tcpWindowClamp"`
|
||||||
TCPMaxSeg int32 `json:"tcpMaxSeg"`
|
TCPMaxSeg int32 `json:"tcpMaxSeg"`
|
||||||
TcpNoDelay bool `json:"tcpNoDelay"`
|
TcpNoDelay bool `json:"tcpNoDelay"`
|
||||||
TCPUserTimeout int32 `json:"tcpUserTimeout"`
|
TCPUserTimeout int32 `json:"tcpUserTimeout"`
|
||||||
V6only bool `json:"v6only"`
|
V6only bool `json:"v6only"`
|
||||||
Interface string `json:"interface"`
|
Interface string `json:"interface"`
|
||||||
TcpMptcp bool `json:"tcpMptcp"`
|
TcpMptcp bool `json:"tcpMptcp"`
|
||||||
|
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -759,6 +767,18 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy)
|
return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var customSockopts []*internet.CustomSockopt
|
||||||
|
|
||||||
|
for _, copt := range c.CustomSockopt {
|
||||||
|
customSockopt := &internet.CustomSockopt{
|
||||||
|
Level: copt.Level,
|
||||||
|
Opt: copt.Opt,
|
||||||
|
Value: copt.Value,
|
||||||
|
Type: copt.Type,
|
||||||
|
}
|
||||||
|
customSockopts = append(customSockopts, customSockopt)
|
||||||
|
}
|
||||||
|
|
||||||
return &internet.SocketConfig{
|
return &internet.SocketConfig{
|
||||||
Mark: c.Mark,
|
Mark: c.Mark,
|
||||||
Tfo: tfo,
|
Tfo: tfo,
|
||||||
@@ -776,6 +796,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
V6Only: c.V6only,
|
V6Only: c.V6only,
|
||||||
Interface: c.Interface,
|
Interface: c.Interface,
|
||||||
TcpMptcp: c.TcpMptcp,
|
TcpMptcp: c.TcpMptcp,
|
||||||
|
CustomSockopt: customSockopts,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
// CommandEnvHolder is a struct holds the environment info of commands
|
// CommandEnvHolder is a struct holds the environment info of commands
|
||||||
type CommandEnvHolder struct {
|
type CommandEnvHolder struct {
|
||||||
// Excutable name of current binary
|
// Executable name of current binary
|
||||||
Exec string
|
Exec string
|
||||||
// commands column width of current command
|
// commands column width of current command
|
||||||
CommandsWidth int
|
CommandsWidth int
|
||||||
|
@@ -43,7 +43,7 @@ func init() {
|
|||||||
case io.Reader:
|
case io.Reader:
|
||||||
return serial.LoadJSONConfig(v)
|
return serial.LoadJSONConfig(v)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknow type")
|
return nil, errors.New("unknown type")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@@ -43,7 +43,7 @@ func init() {
|
|||||||
case io.Reader:
|
case io.Reader:
|
||||||
return serial.LoadTOMLConfig(v)
|
return serial.LoadTOMLConfig(v)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknow type")
|
return nil, errors.New("unknown type")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@@ -43,7 +43,7 @@ func init() {
|
|||||||
case io.Reader:
|
case io.Reader:
|
||||||
return serial.LoadYAMLConfig(v)
|
return serial.LoadYAMLConfig(v)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknow type")
|
return nil, errors.New("unknown type")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@@ -243,7 +243,7 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
return w.Writer.WriteMultiBuffer(mb)
|
return w.Writer.WriteMultiBuffer(mb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReshapeMultiBuffer prepare multi buffer for padding stucture (max 21 bytes)
|
// ReshapeMultiBuffer prepare multi buffer for padding structure (max 21 bytes)
|
||||||
func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBuffer {
|
func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBuffer {
|
||||||
needReshape := 0
|
needReshape := 0
|
||||||
for _, b := range buffer {
|
for _, b := range buffer {
|
||||||
@@ -278,7 +278,7 @@ func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBu
|
|||||||
return mb2
|
return mb2
|
||||||
}
|
}
|
||||||
|
|
||||||
// XtlsPadding add padding to eliminate length siganature during tls handshake
|
// XtlsPadding add padding to eliminate length signature during tls handshake
|
||||||
func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer {
|
func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer {
|
||||||
var contentLen int32 = 0
|
var contentLen int32 = 0
|
||||||
var paddingLen int32 = 0
|
var paddingLen int32 = 0
|
||||||
|
@@ -26,7 +26,7 @@ const (
|
|||||||
type AuthType int32
|
type AuthType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// NO_AUTH is for anounymous authentication.
|
// NO_AUTH is for anonymous authentication.
|
||||||
AuthType_NO_AUTH AuthType = 0
|
AuthType_NO_AUTH AuthType = 0
|
||||||
// PASSWORD is for username/password authentication.
|
// PASSWORD is for username/password authentication.
|
||||||
AuthType_PASSWORD AuthType = 1
|
AuthType_PASSWORD AuthType = 1
|
||||||
|
@@ -17,7 +17,7 @@ message Account {
|
|||||||
|
|
||||||
// AuthType is the authentication type of Socks proxy.
|
// AuthType is the authentication type of Socks proxy.
|
||||||
enum AuthType {
|
enum AuthType {
|
||||||
// NO_AUTH is for anounymous authentication.
|
// NO_AUTH is for anonymous authentication.
|
||||||
NO_AUTH = 0;
|
NO_AUTH = 0;
|
||||||
// PASSWORD is for username/password authentication.
|
// PASSWORD is for username/password authentication.
|
||||||
PASSWORD = 1;
|
PASSWORD = 1;
|
||||||
|
@@ -68,7 +68,7 @@ func TestReadUsernamePassword(t *testing.T) {
|
|||||||
t.Error("for input: ", testCase.Input, " expect username ", testCase.Username, " but actually ", username)
|
t.Error("for input: ", testCase.Input, " expect username ", testCase.Username, " but actually ", username)
|
||||||
}
|
}
|
||||||
if testCase.Password != password {
|
if testCase.Password != password {
|
||||||
t.Error("for input: ", testCase.Input, " expect passowrd ", testCase.Password, " but actually ", password)
|
t.Error("for input: ", testCase.Input, " expect password ", testCase.Password, " but actually ", password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
In the sock implementation of * ray, UDP authentication is flawed and can be bypassed.
|
In the sock implementation of * ray, UDP authentication is flawed and can be bypassed.
|
||||||
Tracking a UDP connection may be a bit troublesome.
|
Tracking a UDP connection may be a bit troublesome.
|
||||||
Here is a simple solution.
|
Here is a simple solution.
|
||||||
We creat a filter, add remote IP to the pool when it try to establish a UDP connection with auth.
|
We create a filter, add remote IP to the pool when it try to establish a UDP connection with auth.
|
||||||
And drop UDP packets from unauthorized IP.
|
And drop UDP packets from unauthorized IP.
|
||||||
After discussion, we believe it is not necessary to add a timeout mechanism to this filter.
|
After discussion, we believe it is not necessary to add a timeout mechanism to this filter.
|
||||||
*/
|
*/
|
||||||
|
@@ -124,7 +124,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
|||||||
return errors.New("failed to write A request payload").Base(err).AtWarning()
|
return errors.New("failed to write A request payload").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
|
// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
|
||||||
if err = bufferWriter.SetBuffered(false); err != nil {
|
if err = bufferWriter.SetBuffered(false); err != nil {
|
||||||
return errors.New("failed to flush payload").Base(err).AtWarning()
|
return errors.New("failed to flush payload").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
|
@@ -223,14 +223,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
cs := tlsConn.ConnectionState()
|
cs := tlsConn.ConnectionState()
|
||||||
name = cs.ServerName
|
name = cs.ServerName
|
||||||
alpn = cs.NegotiatedProtocol
|
alpn = cs.NegotiatedProtocol
|
||||||
errors.LogInfo(ctx, "realName = " + name)
|
errors.LogInfo(ctx, "realName = "+name)
|
||||||
errors.LogInfo(ctx, "realAlpn = " + alpn)
|
errors.LogInfo(ctx, "realAlpn = "+alpn)
|
||||||
} else if realityConn, ok := iConn.(*reality.Conn); ok {
|
} else if realityConn, ok := iConn.(*reality.Conn); ok {
|
||||||
cs := realityConn.ConnectionState()
|
cs := realityConn.ConnectionState()
|
||||||
name = cs.ServerName
|
name = cs.ServerName
|
||||||
alpn = cs.NegotiatedProtocol
|
alpn = cs.NegotiatedProtocol
|
||||||
errors.LogInfo(ctx, "realName = " + name)
|
errors.LogInfo(ctx, "realName = "+name)
|
||||||
errors.LogInfo(ctx, "realAlpn = " + alpn)
|
errors.LogInfo(ctx, "realAlpn = "+alpn)
|
||||||
}
|
}
|
||||||
name = strings.ToLower(name)
|
name = strings.ToLower(name)
|
||||||
alpn = strings.ToLower(alpn)
|
alpn = strings.ToLower(alpn)
|
||||||
@@ -295,7 +295,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
}
|
}
|
||||||
if k == '?' || k == ' ' {
|
if k == '?' || k == ' ' {
|
||||||
path = string(firstBytes[i:j])
|
path = string(firstBytes[i:j])
|
||||||
errors.LogInfo(ctx, "realPath = " + path)
|
errors.LogInfo(ctx, "realPath = "+path)
|
||||||
if pfb[path] == nil {
|
if pfb[path] == nil {
|
||||||
path = ""
|
path = ""
|
||||||
}
|
}
|
||||||
@@ -524,7 +524,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
clientReader = proxy.NewVisionReader(clientReader, trafficState, ctx1)
|
clientReader = proxy.NewVisionReader(clientReader, trafficState, ctx1)
|
||||||
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, ctx1)
|
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, ctx1)
|
||||||
} else {
|
} else {
|
||||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
|
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
|
||||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +552,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil {
|
if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil {
|
||||||
return err // ...
|
return err // ...
|
||||||
}
|
}
|
||||||
// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
|
// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
|
||||||
if err := bufferWriter.SetBuffered(false); err != nil {
|
if err := bufferWriter.SetBuffered(false); err != nil {
|
||||||
return errors.New("failed to write A response payload").Base(err).AtWarning()
|
return errors.New("failed to write A response payload").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
@@ -561,7 +561,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
if requestAddons.Flow == vless.XRV {
|
if requestAddons.Flow == vless.XRV {
|
||||||
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, ctx)
|
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, ctx)
|
||||||
} else {
|
} else {
|
||||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
|
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
|
||||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -219,7 +219,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
} else {
|
} else {
|
||||||
errors.LogDebug(ctx, "Reader is not timeout reader, will send out vless header separately from first payload")
|
errors.LogDebug(ctx, "Reader is not timeout reader, will send out vless header separately from first payload")
|
||||||
}
|
}
|
||||||
// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
|
// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
|
||||||
if err := bufferWriter.SetBuffered(false); err != nil {
|
if err := bufferWriter.SetBuffered(false); err != nil {
|
||||||
return errors.New("failed to write A request payload").Base(err).AtWarning()
|
return errors.New("failed to write A request payload").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
||||||
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, ctx1)
|
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, ctx1)
|
||||||
} else {
|
} else {
|
||||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
|
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
|
||||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -276,7 +276,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
if requestAddons.Flow == vless.XRV {
|
if requestAddons.Flow == vless.XRV {
|
||||||
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, ctx)
|
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, ctx)
|
||||||
} else {
|
} else {
|
||||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
|
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
|
||||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
121
transport/internet/browser_dialer/dialer.go
Normal file
121
transport/internet/browser_dialer/dialer.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package browser_dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/platform"
|
||||||
|
"github.com/xtls/xray-core/common/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed dialer.html
|
||||||
|
var webpage []byte
|
||||||
|
|
||||||
|
var conns chan *websocket.Conn
|
||||||
|
|
||||||
|
var upgrader = &websocket.Upgrader{
|
||||||
|
ReadBufferSize: 0,
|
||||||
|
WriteBufferSize: 0,
|
||||||
|
HandshakeTimeout: time.Second * 4,
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
addr := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" })
|
||||||
|
if addr != "" {
|
||||||
|
token := uuid.New()
|
||||||
|
csrfToken := token.String()
|
||||||
|
webpage = bytes.ReplaceAll(webpage, []byte("csrfToken"), []byte(csrfToken))
|
||||||
|
conns = make(chan *websocket.Conn, 256)
|
||||||
|
go http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path == "/websocket" {
|
||||||
|
if r.URL.Query().Get("token") == csrfToken {
|
||||||
|
if conn, err := upgrader.Upgrade(w, r, nil); err == nil {
|
||||||
|
conns <- conn
|
||||||
|
} else {
|
||||||
|
errors.LogError(context.Background(), "Browser dialer http upgrade unexpected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Write(webpage)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasBrowserDialer() bool {
|
||||||
|
return conns != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialWS(uri string, ed []byte) (*websocket.Conn, error) {
|
||||||
|
data := []byte("WS " + uri)
|
||||||
|
if ed != nil {
|
||||||
|
data = append(data, " "+base64.RawURLEncoding.EncodeToString(ed)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialRaw(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialGet(uri string) (*websocket.Conn, error) {
|
||||||
|
data := []byte("GET " + uri)
|
||||||
|
return dialRaw(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialPost(uri string, payload []byte) error {
|
||||||
|
data := []byte("POST " + uri)
|
||||||
|
conn, err := dialRaw(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.WriteMessage(websocket.BinaryMessage, payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = CheckOK(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialRaw(data []byte) (*websocket.Conn, error) {
|
||||||
|
var conn *websocket.Conn
|
||||||
|
for {
|
||||||
|
conn = <-conns
|
||||||
|
if conn.WriteMessage(websocket.TextMessage, data) != nil {
|
||||||
|
conn.Close()
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := CheckOK(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckOK(conn *websocket.Conn) error {
|
||||||
|
if _, p, err := conn.ReadMessage(); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return err
|
||||||
|
} else if s := string(p); s != "ok" {
|
||||||
|
conn.Close()
|
||||||
|
return errors.New(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
136
transport/internet/browser_dialer/dialer.html
Normal file
136
transport/internet/browser_dialer/dialer.html
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Browser Dialer</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// Copyright (c) 2021 XRAY. Mozilla Public License 2.0.
|
||||||
|
var url = "ws://" + window.location.host + "/websocket?token=csrfToken";
|
||||||
|
var clientIdleCount = 0;
|
||||||
|
var upstreamGetCount = 0;
|
||||||
|
var upstreamWsCount = 0;
|
||||||
|
var upstreamPostCount = 0;
|
||||||
|
setInterval(check, 1000);
|
||||||
|
function check() {
|
||||||
|
if (clientIdleCount > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientIdleCount += 1;
|
||||||
|
console.log("Prepare", url);
|
||||||
|
var ws = new WebSocket(url);
|
||||||
|
// arraybuffer is significantly faster in chrome than default
|
||||||
|
// blob, tested with chrome 123
|
||||||
|
ws.binaryType = "arraybuffer";
|
||||||
|
ws.onmessage = function (event) {
|
||||||
|
clientIdleCount -= 1;
|
||||||
|
let [method, url, protocol] = event.data.split(" ");
|
||||||
|
if (method == "WS") {
|
||||||
|
upstreamWsCount += 1;
|
||||||
|
console.log("Dial WS", url, protocol);
|
||||||
|
const wss = new WebSocket(url, protocol);
|
||||||
|
wss.binaryType = "arraybuffer";
|
||||||
|
var opened = false;
|
||||||
|
ws.onmessage = function (event) {
|
||||||
|
wss.send(event.data)
|
||||||
|
}
|
||||||
|
wss.onopen = function (event) {
|
||||||
|
opened = true;
|
||||||
|
ws.send("ok")
|
||||||
|
}
|
||||||
|
wss.onmessage = function (event) {
|
||||||
|
ws.send(event.data)
|
||||||
|
}
|
||||||
|
wss.onclose = function (event) {
|
||||||
|
upstreamWsCount -= 1;
|
||||||
|
console.log("Dial WS DONE, remaining: ", upstreamWsCount);
|
||||||
|
ws.close()
|
||||||
|
}
|
||||||
|
wss.onerror = function (event) {
|
||||||
|
!opened && ws.send("fail")
|
||||||
|
wss.close()
|
||||||
|
}
|
||||||
|
ws.onclose = function (event) {
|
||||||
|
wss.close()
|
||||||
|
}
|
||||||
|
} else if (method == "GET") {
|
||||||
|
(async () => {
|
||||||
|
console.log("Dial GET", url);
|
||||||
|
ws.send("ok");
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Aborting a streaming response in JavaScript
|
||||||
|
requires two levers to be pulled:
|
||||||
|
|
||||||
|
First, the streaming read itself has to be cancelled using
|
||||||
|
reader.cancel(), only then controller.abort() will actually work.
|
||||||
|
|
||||||
|
If controller.abort() alone is called while a
|
||||||
|
reader.read() is ongoing, it will block until the server closes the
|
||||||
|
response, the page is refreshed or the network connection is lost.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let reader = null;
|
||||||
|
ws.onclose = (event) => {
|
||||||
|
try {
|
||||||
|
reader && reader.cancel();
|
||||||
|
} catch(e) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
controller.abort();
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
upstreamGetCount += 1;
|
||||||
|
const response = await fetch(url, {signal: controller.signal});
|
||||||
|
|
||||||
|
const body = await response.body;
|
||||||
|
reader = body.getReader();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
ws.send(value);
|
||||||
|
if (done) break;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
upstreamGetCount -= 1;
|
||||||
|
console.log("Dial GET DONE, remaining: ", upstreamGetCount);
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
} else if (method == "POST") {
|
||||||
|
upstreamPostCount += 1;
|
||||||
|
console.log("Dial POST", url);
|
||||||
|
ws.send("ok");
|
||||||
|
ws.onmessage = async (event) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
url,
|
||||||
|
{method: "POST", body: event.data}
|
||||||
|
);
|
||||||
|
if (response.ok) {
|
||||||
|
ws.send("ok");
|
||||||
|
} else {
|
||||||
|
console.error("bad status code");
|
||||||
|
ws.send("fail");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
upstreamPostCount -= 1;
|
||||||
|
console.log("Dial POST DONE, remaining: ", upstreamPostCount);
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
check()
|
||||||
|
}
|
||||||
|
ws.onerror = function (event) {
|
||||||
|
ws.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.34.1
|
// protoc-gen-go v1.34.2
|
||||||
// protoc v5.27.0
|
// protoc v5.27.2
|
||||||
// source: transport/internet/config.proto
|
// source: transport/internet/config.proto
|
||||||
|
|
||||||
package internet
|
package internet
|
||||||
@@ -207,7 +207,7 @@ func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
|
|||||||
|
|
||||||
// Deprecated: Use SocketConfig_TProxyMode.Descriptor instead.
|
// Deprecated: Use SocketConfig_TProxyMode.Descriptor instead.
|
||||||
func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
|
func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3, 0}
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{4, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TransportConfig struct {
|
type TransportConfig struct {
|
||||||
@@ -429,6 +429,77 @@ func (x *ProxyConfig) GetTransportLayerProxy() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomSockopt struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Level string `protobuf:"bytes,1,opt,name=level,proto3" json:"level,omitempty"`
|
||||||
|
Opt string `protobuf:"bytes,2,opt,name=opt,proto3" json:"opt,omitempty"`
|
||||||
|
Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CustomSockopt) Reset() {
|
||||||
|
*x = CustomSockopt{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CustomSockopt) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CustomSockopt) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||||
|
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 CustomSockopt.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CustomSockopt) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CustomSockopt) GetLevel() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Level
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CustomSockopt) GetOpt() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Opt
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CustomSockopt) GetValue() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CustomSockopt) GetType() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Type
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// SocketConfig is options to be applied on network sockets.
|
// SocketConfig is options to be applied on network sockets.
|
||||||
type SocketConfig struct {
|
type SocketConfig struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
@@ -443,28 +514,29 @@ type SocketConfig struct {
|
|||||||
Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=xray.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"`
|
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
|
// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
|
||||||
// option. This option is for UDP only.
|
// 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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
Interface string `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"`
|
||||||
V6Only bool `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,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"`
|
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"`
|
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"`
|
TcpMaxSeg int32 `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"`
|
||||||
TcpNoDelay bool `protobuf:"varint,18,opt,name=tcp_no_delay,json=tcpNoDelay,proto3" json:"tcp_no_delay,omitempty"`
|
TcpNoDelay bool `protobuf:"varint,18,opt,name=tcp_no_delay,json=tcpNoDelay,proto3" json:"tcp_no_delay,omitempty"`
|
||||||
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
|
||||||
|
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SocketConfig) Reset() {
|
func (x *SocketConfig) Reset() {
|
||||||
*x = SocketConfig{}
|
*x = SocketConfig{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -477,7 +549,7 @@ func (x *SocketConfig) String() string {
|
|||||||
func (*SocketConfig) ProtoMessage() {}
|
func (*SocketConfig) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -490,7 +562,7 @@ func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead.
|
// Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead.
|
||||||
func (*SocketConfig) Descriptor() ([]byte, []int) {
|
func (*SocketConfig) Descriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SocketConfig) GetMark() int32 {
|
func (x *SocketConfig) GetMark() int32 {
|
||||||
@@ -626,6 +698,13 @@ func (x *SocketConfig) GetTcpMptcp() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SocketConfig) GetCustomSockopt() []*CustomSockopt {
|
||||||
|
if x != nil {
|
||||||
|
return x.CustomSockopt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_transport_internet_config_proto_rawDesc = []byte{
|
var file_transport_internet_config_proto_rawDesc = []byte{
|
||||||
@@ -678,85 +757,96 @@ var file_transport_internet_config_proto_rawDesc = []byte{
|
|||||||
0x12, 0x30, 0x0a, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79,
|
0x12, 0x30, 0x0a, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79,
|
||||||
0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74,
|
0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74,
|
||||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f,
|
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f,
|
||||||
0x78, 0x79, 0x22, 0xd1, 0x06, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
0x78, 0x79, 0x22, 0x61, 0x0a, 0x0d, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b,
|
||||||
0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02,
|
0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x70, 0x74,
|
||||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76,
|
||||||
0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||||
0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x9f, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74,
|
||||||
0x2e, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x70, 0x72,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01,
|
||||||
0x6f, 0x78, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f,
|
0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66,
|
||||||
0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64,
|
0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06,
|
||||||
0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65,
|
0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x78,
|
||||||
0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x41,
|
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
||||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61,
|
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
||||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69,
|
0x66, 0x69, 0x67, 0x2e, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06,
|
||||||
0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e,
|
0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76,
|
||||||
0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x69,
|
0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f,
|
||||||
0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74,
|
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72,
|
||||||
0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
|
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65,
|
||||||
0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f,
|
0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e,
|
||||||
0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, 0x0a, 0x0f, 0x64, 0x6f,
|
0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08, 0x20,
|
0x0b, 0x62, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09,
|
||||||
0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||||
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f,
|
0x08, 0x62, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63,
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f,
|
0x65, 0x70, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x21, 0x0a, 0x0c,
|
0x6f, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74,
|
||||||
0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x09, 0x20, 0x01,
|
0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, 0x0a,
|
||||||
0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12,
|
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||||
0x35, 0x0a, 0x17, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76,
|
0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
|
||||||
0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05,
|
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||||
0x52, 0x14, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x49, 0x6e,
|
0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52,
|
||||||
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65,
|
0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12,
|
||||||
0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x0b, 0x20,
|
0x21, 0x0a, 0x0c, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18,
|
||||||
0x01, 0x28, 0x05, 0x52, 0x10, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76,
|
0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f,
|
||||||
0x65, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e,
|
0x78, 0x79, 0x12, 0x35, 0x0a, 0x17, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61,
|
||||||
0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74,
|
0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20,
|
||||||
0x63, 0x70, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09,
|
0x01, 0x28, 0x05, 0x52, 0x14, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76,
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70,
|
||||||
0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x36,
|
0x5f, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x6c, 0x65,
|
||||||
0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x36, 0x6f, 0x6e,
|
0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41,
|
||||||
0x6c, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77,
|
0x6c, 0x69, 0x76, 0x65, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f,
|
||||||
0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x63,
|
0x63, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x70, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x43, 0x6c, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10,
|
0x52, 0x0d, 0x74, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||||
0x74, 0x63, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
|
0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01,
|
||||||
0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x63, 0x70, 0x55, 0x73, 0x65, 0x72, 0x54,
|
0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a,
|
||||||
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x74, 0x63, 0x70, 0x5f, 0x6d, 0x61,
|
0x06, 0x76, 0x36, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76,
|
||||||
0x78, 0x5f, 0x73, 0x65, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x63, 0x70,
|
0x36, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x77, 0x69, 0x6e,
|
||||||
0x4d, 0x61, 0x78, 0x53, 0x65, 0x67, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x63, 0x70, 0x5f, 0x6e, 0x6f,
|
0x64, 0x6f, 0x77, 0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||||
0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x74, 0x63,
|
0x0e, 0x74, 0x63, 0x70, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x43, 0x6c, 0x61, 0x6d, 0x70, 0x12,
|
||||||
0x70, 0x4e, 0x6f, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f,
|
0x28, 0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||||
0x6d, 0x70, 0x74, 0x63, 0x70, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x63, 0x70,
|
0x6f, 0x75, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x63, 0x70, 0x55, 0x73,
|
||||||
0x4d, 0x70, 0x74, 0x63, 0x70, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d,
|
0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x74, 0x63, 0x70,
|
||||||
0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
|
0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09,
|
||||||
0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69,
|
0x74, 0x63, 0x70, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x67, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x63, 0x70,
|
||||||
0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x7a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
|
0x5f, 0x6e, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54,
|
0x0a, 0x74, 0x63, 0x70, 0x4e, 0x6f, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x74,
|
||||||
0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a,
|
0x63, 0x70, 0x5f, 0x6d, 0x70, 0x74, 0x63, 0x70, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
|
||||||
0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f,
|
0x74, 0x63, 0x70, 0x4d, 0x70, 0x74, 0x63, 0x70, 0x12, 0x4c, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74,
|
||||||
0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04,
|
0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74,
|
0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64,
|
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
|
||||||
0x65, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x54, 0x54, 0x50,
|
0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53,
|
||||||
0x10, 0x07, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72,
|
0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79,
|
||||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00,
|
0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a,
|
||||||
0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,
|
0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64,
|
||||||
0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45,
|
0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x7a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73,
|
||||||
0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50,
|
0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03,
|
||||||
0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34,
|
0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08,
|
||||||
0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06,
|
0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53,
|
||||||
0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12,
|
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10,
|
||||||
0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e,
|
0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65,
|
||||||
0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e,
|
0x74, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x55, 0x70, 0x67, 0x72, 0x61,
|
||||||
0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x67,
|
0x64, 0x65, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x54, 0x54,
|
||||||
0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
0x50, 0x10, 0x07, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
|
||||||
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a,
|
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10,
|
||||||
0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73,
|
0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a,
|
||||||
0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53,
|
||||||
0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17,
|
0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49,
|
||||||
0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49,
|
0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36,
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -772,31 +862,33 @@ 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_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||||
var file_transport_internet_config_proto_goTypes = []interface{}{
|
var file_transport_internet_config_proto_goTypes = []any{
|
||||||
(TransportProtocol)(0), // 0: xray.transport.internet.TransportProtocol
|
(TransportProtocol)(0), // 0: xray.transport.internet.TransportProtocol
|
||||||
(DomainStrategy)(0), // 1: xray.transport.internet.DomainStrategy
|
(DomainStrategy)(0), // 1: xray.transport.internet.DomainStrategy
|
||||||
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
||||||
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
|
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
|
||||||
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
||||||
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
|
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
|
||||||
(*SocketConfig)(nil), // 6: xray.transport.internet.SocketConfig
|
(*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt
|
||||||
(*serial.TypedMessage)(nil), // 7: xray.common.serial.TypedMessage
|
(*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig
|
||||||
|
(*serial.TypedMessage)(nil), // 8: xray.common.serial.TypedMessage
|
||||||
}
|
}
|
||||||
var file_transport_internet_config_proto_depIdxs = []int32{
|
var file_transport_internet_config_proto_depIdxs = []int32{
|
||||||
0, // 0: xray.transport.internet.TransportConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
|
0, // 0: xray.transport.internet.TransportConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
|
||||||
7, // 1: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
8, // 1: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||||
0, // 2: xray.transport.internet.StreamConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
|
0, // 2: xray.transport.internet.StreamConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
|
||||||
3, // 3: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
3, // 3: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||||
7, // 4: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
8, // 4: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||||
6, // 5: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
7, // 5: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||||
2, // 6: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
2, // 6: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||||
1, // 7: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
1, // 7: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||||
8, // [8:8] is the sub-list for method output_type
|
6, // 8: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||||
8, // [8:8] is the sub-list for method input_type
|
9, // [9:9] is the sub-list for method output_type
|
||||||
8, // [8:8] is the sub-list for extension type_name
|
9, // [9:9] is the sub-list for method input_type
|
||||||
8, // [8:8] is the sub-list for extension extendee
|
9, // [9:9] is the sub-list for extension type_name
|
||||||
0, // [0:8] is the sub-list for field type_name
|
9, // [9:9] is the sub-list for extension extendee
|
||||||
|
0, // [0:9] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_transport_internet_config_proto_init() }
|
func init() { file_transport_internet_config_proto_init() }
|
||||||
@@ -805,7 +897,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
if !protoimpl.UnsafeEnabled {
|
||||||
file_transport_internet_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
file_transport_internet_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*TransportConfig); i {
|
switch v := v.(*TransportConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -817,7 +909,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_transport_internet_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
file_transport_internet_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*StreamConfig); i {
|
switch v := v.(*StreamConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -829,7 +921,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_transport_internet_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
file_transport_internet_config_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*ProxyConfig); i {
|
switch v := v.(*ProxyConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -841,7 +933,19 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_transport_internet_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
file_transport_internet_config_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*CustomSockopt); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_transport_internet_config_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*SocketConfig); i {
|
switch v := v.(*SocketConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -860,7 +964,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_transport_internet_config_proto_rawDesc,
|
RawDescriptor: file_transport_internet_config_proto_rawDesc,
|
||||||
NumEnums: 3,
|
NumEnums: 3,
|
||||||
NumMessages: 4,
|
NumMessages: 5,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
@@ -68,6 +68,13 @@ message ProxyConfig {
|
|||||||
bool transportLayerProxy = 2;
|
bool transportLayerProxy = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CustomSockopt {
|
||||||
|
string level = 1;
|
||||||
|
string opt = 2;
|
||||||
|
string value = 3;
|
||||||
|
string type = 4;
|
||||||
|
}
|
||||||
|
|
||||||
// SocketConfig is options to be applied on network sockets.
|
// SocketConfig is options to be applied on network sockets.
|
||||||
message SocketConfig {
|
message SocketConfig {
|
||||||
// Mark of the connection. If non-zero, the value will be set to SO_MARK.
|
// Mark of the connection. If non-zero, the value will be set to SO_MARK.
|
||||||
@@ -121,4 +128,6 @@ message SocketConfig {
|
|||||||
bool tcp_no_delay = 18;
|
bool tcp_no_delay = 18;
|
||||||
|
|
||||||
bool tcp_mptcp = 19;
|
bool tcp_mptcp = 19;
|
||||||
|
|
||||||
|
repeated CustomSockopt customSockopt = 20;
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ type Config struct {
|
|||||||
// Path of the domain socket. This overrides the IP/Port parameter from
|
// Path of the domain socket. This overrides the IP/Port parameter from
|
||||||
// upstream caller.
|
// upstream caller.
|
||||||
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
|
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
|
||||||
// Abstract speicifies whether to use abstract namespace or not.
|
// Abstract specifies whether to use abstract namespace or not.
|
||||||
// Traditionally Unix domain socket is file system based. Abstract domain
|
// Traditionally Unix domain socket is file system based. Abstract domain
|
||||||
// socket can be used without acquiring file lock.
|
// socket can be used without acquiring file lock.
|
||||||
Abstract bool `protobuf:"varint,2,opt,name=abstract,proto3" json:"abstract,omitempty"`
|
Abstract bool `protobuf:"varint,2,opt,name=abstract,proto3" json:"abstract,omitempty"`
|
||||||
|
@@ -10,7 +10,7 @@ message Config {
|
|||||||
// Path of the domain socket. This overrides the IP/Port parameter from
|
// Path of the domain socket. This overrides the IP/Port parameter from
|
||||||
// upstream caller.
|
// upstream caller.
|
||||||
string path = 1;
|
string path = 1;
|
||||||
// Abstract speicifies whether to use abstract namespace or not.
|
// Abstract specifies whether to use abstract namespace or not.
|
||||||
// Traditionally Unix domain socket is file system based. Abstract domain
|
// Traditionally Unix domain socket is file system based. Abstract domain
|
||||||
// socket can be used without acquiring file lock.
|
// socket can be used without acquiring file lock.
|
||||||
bool abstract = 2;
|
bool abstract = 2;
|
||||||
|
@@ -374,7 +374,7 @@ type Config struct {
|
|||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
// Settings for authenticating requests. If not set, client side will not send
|
// Settings for authenticating requests. If not set, client side will not send
|
||||||
// authenication header, and server side will bypass authentication.
|
// authentication header, and server side will bypass authentication.
|
||||||
Request *RequestConfig `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"`
|
Request *RequestConfig `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"`
|
||||||
// Settings for authenticating responses. If not set, client side will bypass
|
// Settings for authenticating responses. If not set, client side will bypass
|
||||||
// authentication, and server side will not send authentication header.
|
// authentication, and server side will not send authentication header.
|
||||||
|
@@ -56,7 +56,7 @@ message ResponseConfig {
|
|||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
// Settings for authenticating requests. If not set, client side will not send
|
// Settings for authenticating requests. If not set, client side will not send
|
||||||
// authenication header, and server side will bypass authentication.
|
// authentication header, and server side will bypass authentication.
|
||||||
RequestConfig request = 1;
|
RequestConfig request = 1;
|
||||||
|
|
||||||
// Settings for authenticating responses. If not set, client side will bypass
|
// Settings for authenticating responses. If not set, client side will bypass
|
||||||
|
@@ -16,9 +16,12 @@ func (c *Config) getHosts() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) isValidHost(host string) bool {
|
func (c *Config) isValidHost(host string) bool {
|
||||||
|
if len(c.Host) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
hosts := c.getHosts()
|
hosts := c.getHosts()
|
||||||
for _, h := range hosts {
|
for _, h := range hosts {
|
||||||
if h == host {
|
if internet.IsValidHTTPHost(host, h) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,18 +2,18 @@ package httpupgrade
|
|||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
type connnection struct {
|
type connection struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
remoteAddr net.Addr
|
remoteAddr net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConnection(conn net.Conn, remoteAddr net.Addr) *connnection {
|
func newConnection(conn net.Conn, remoteAddr net.Addr) *connection {
|
||||||
return &connnection{
|
return &connection{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
remoteAddr: remoteAddr,
|
remoteAddr: remoteAddr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *connnection) RemoteAddr() net.Addr {
|
func (c *connection) RemoteAddr() net.Addr {
|
||||||
return c.remoteAddr
|
return c.remoteAddr
|
||||||
}
|
}
|
||||||
|
@@ -151,7 +151,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.Write([]byte("Response"))
|
_, err = c.Write([]byte(c.RemoteAddr().String()))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
}(conn)
|
}(conn)
|
||||||
})
|
})
|
||||||
@@ -169,7 +169,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
|||||||
var b [1024]byte
|
var b [1024]byte
|
||||||
n, err := conn.Read(b[:])
|
n, err := conn.Read(b[:])
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if string(b[:n]) != "Response" {
|
if string(b[:n]) != "1.1.1.1:0" {
|
||||||
t.Error("response: ", string(b[:n]))
|
t.Error("response: ", string(b[:n]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,7 +39,7 @@ func (s *server) Handle(conn net.Conn) (stat.Connection, error) {
|
|||||||
|
|
||||||
if s.config != nil {
|
if s.config != nil {
|
||||||
host := req.Host
|
host := req.Host
|
||||||
if len(s.config.Host) > 0 && host != s.config.Host {
|
if len(s.config.Host) > 0 && !internet.IsValidHTTPHost(host, s.config.Host) {
|
||||||
return nil, errors.New("bad host: ", host)
|
return nil, errors.New("bad host: ", host)
|
||||||
}
|
}
|
||||||
path := s.config.GetNormalizedPath()
|
path := s.config.GetNormalizedPath()
|
||||||
|
@@ -1,3 +1,18 @@
|
|||||||
package internet
|
package internet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||||
|
|
||||||
|
func IsValidHTTPHost(request string, config string) bool {
|
||||||
|
r := strings.ToLower(request)
|
||||||
|
c := strings.ToLower(config)
|
||||||
|
if strings.Contains(r, ":") {
|
||||||
|
h, _, _ := net.SplitHostPort(r)
|
||||||
|
return h == c
|
||||||
|
}
|
||||||
|
return r == c
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package internet
|
package internet
|
||||||
|
|
||||||
// MemoryStreamConfig is a parsed form of StreamConfig. This is used to reduce number of Protobuf parsing.
|
// MemoryStreamConfig is a parsed form of StreamConfig. This is used to reduce the number of Protobuf parsings.
|
||||||
type MemoryStreamConfig struct {
|
type MemoryStreamConfig struct {
|
||||||
ProtocolName string
|
ProtocolName string
|
||||||
ProtocolSettings interface{}
|
ProtocolSettings interface{}
|
||||||
|
@@ -2,6 +2,7 @@ package internet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -107,7 +108,32 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
|
|||||||
return errors.New("failed to set TCP_NODELAY", err)
|
return errors.New("failed to set TCP_NODELAY", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(config.CustomSockopt) > 0 {
|
||||||
|
for _, custom := range config.CustomSockopt {
|
||||||
|
var level = 0x6 // default TCP
|
||||||
|
var opt int
|
||||||
|
if len(custom.Opt) == 0 {
|
||||||
|
return errors.New("No opt!")
|
||||||
|
} else {
|
||||||
|
opt, _ = strconv.Atoi(custom.Opt)
|
||||||
|
}
|
||||||
|
if custom.Level != "" {
|
||||||
|
level, _ = strconv.Atoi(custom.Level)
|
||||||
|
}
|
||||||
|
if custom.Type == "int" {
|
||||||
|
value, _ := strconv.Atoi(custom.Value)
|
||||||
|
if err := syscall.SetsockoptInt(int(fd), level, opt, value); err != nil {
|
||||||
|
return errors.New("failed to set CustomSockoptInt", opt, value, err)
|
||||||
|
}
|
||||||
|
} else if custom.Type == "str" {
|
||||||
|
if err := syscall.SetsockoptString(int(fd), level, opt, custom.Value); err != nil {
|
||||||
|
return errors.New("failed to set CustomSockoptString", opt, custom.Value, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errors.New("unknown CustomSockopt type:", custom.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Tproxy.IsEnabled() {
|
if config.Tproxy.IsEnabled() {
|
||||||
@@ -176,6 +202,32 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig)
|
|||||||
return errors.New("failed to set TCP_MAXSEG", err)
|
return errors.New("failed to set TCP_MAXSEG", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(config.CustomSockopt) > 0 {
|
||||||
|
for _, custom := range config.CustomSockopt {
|
||||||
|
var level = 0x6 // default TCP
|
||||||
|
var opt int
|
||||||
|
if len(custom.Opt) == 0 {
|
||||||
|
return errors.New("No opt!")
|
||||||
|
} else {
|
||||||
|
opt, _ = strconv.Atoi(custom.Opt)
|
||||||
|
}
|
||||||
|
if custom.Level != "" {
|
||||||
|
level, _ = strconv.Atoi(custom.Level)
|
||||||
|
}
|
||||||
|
if custom.Type == "int" {
|
||||||
|
value, _ := strconv.Atoi(custom.Value)
|
||||||
|
if err := syscall.SetsockoptInt(int(fd), level, opt, value); err != nil {
|
||||||
|
return errors.New("failed to set CustomSockoptInt", opt, value, err)
|
||||||
|
}
|
||||||
|
} else if custom.Type == "str" {
|
||||||
|
if err := syscall.SetsockoptString(int(fd), level, opt, custom.Value); err != nil {
|
||||||
|
return errors.New("failed to set CustomSockoptString", opt, custom.Value, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errors.New("unknown CustomSockopt type:", custom.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Tproxy.IsEnabled() {
|
if config.Tproxy.IsEnabled() {
|
||||||
|
39
transport/internet/splithttp/browser_client.go
Normal file
39
transport/internet/splithttp/browser_client.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package splithttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
gonet "net"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/transport/internet/browser_dialer"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// implements splithttp.DialerClient in terms of browser dialer
|
||||||
|
// has no fields because everything is global state :O)
|
||||||
|
type BrowserDialerClient struct{}
|
||||||
|
|
||||||
|
func (c *BrowserDialerClient) OpenDownload(ctx context.Context, baseURL string) (io.ReadCloser, gonet.Addr, gonet.Addr, error) {
|
||||||
|
conn, err := browser_dialer.DialGet(baseURL)
|
||||||
|
dummyAddr := &gonet.IPAddr{}
|
||||||
|
if err != nil {
|
||||||
|
return nil, dummyAddr, dummyAddr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return websocket.NewConnection(conn, dummyAddr, nil), conn.RemoteAddr(), conn.LocalAddr(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BrowserDialerClient) SendUploadRequest(ctx context.Context, url string, payload io.ReadWriteCloser, contentLength int64) error {
|
||||||
|
bytes, err := ioutil.ReadAll(payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = browser_dialer.DialPost(url, bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
170
transport/internet/splithttp/client.go
Normal file
170
transport/internet/splithttp/client.go
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
package splithttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
gonet "net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
|
)
|
||||||
|
|
||||||
|
// interface to abstract between use of browser dialer, vs net/http
|
||||||
|
type DialerClient interface {
|
||||||
|
// (ctx, baseURL, payload) -> err
|
||||||
|
// baseURL already contains sessionId and seq
|
||||||
|
SendUploadRequest(context.Context, string, io.ReadWriteCloser, int64) error
|
||||||
|
|
||||||
|
// (ctx, baseURL) -> (downloadReader, remoteAddr, localAddr)
|
||||||
|
// baseURL already contains sessionId
|
||||||
|
OpenDownload(context.Context, string) (io.ReadCloser, net.Addr, net.Addr, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements splithttp.DialerClient in terms of direct network connections
|
||||||
|
type DefaultDialerClient struct {
|
||||||
|
transportConfig *Config
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultDialerClient) OpenDownload(ctx context.Context, baseURL string) (io.ReadCloser, gonet.Addr, gonet.Addr, error) {
|
||||||
|
var remoteAddr gonet.Addr
|
||||||
|
var localAddr gonet.Addr
|
||||||
|
// this is done when the TCP/UDP connection to the server was established,
|
||||||
|
// and we can unblock the Dial function and print correct net addresses in
|
||||||
|
// logs
|
||||||
|
gotConn := done.New()
|
||||||
|
|
||||||
|
var downResponse io.ReadCloser
|
||||||
|
gotDownResponse := done.New()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
trace := &httptrace.ClientTrace{
|
||||||
|
GotConn: func(connInfo httptrace.GotConnInfo) {
|
||||||
|
remoteAddr = connInfo.Conn.RemoteAddr()
|
||||||
|
localAddr = connInfo.Conn.LocalAddr()
|
||||||
|
gotConn.Close()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case we hit an error, we want to unblock this part
|
||||||
|
defer gotConn.Close()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(
|
||||||
|
httptrace.WithClientTrace(ctx, trace),
|
||||||
|
"GET",
|
||||||
|
baseURL,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(ctx, err, "failed to construct download http request")
|
||||||
|
gotDownResponse.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header = c.transportConfig.GetRequestHeader()
|
||||||
|
|
||||||
|
response, err := c.download.Do(req)
|
||||||
|
gotConn.Close()
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(ctx, err, "failed to send download http request")
|
||||||
|
gotDownResponse.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode != 200 {
|
||||||
|
response.Body.Close()
|
||||||
|
errors.LogInfo(ctx, "invalid status code on download:", response.Status)
|
||||||
|
gotDownResponse.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
downResponse = response.Body
|
||||||
|
gotDownResponse.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// we want to block Dial until we know the remote address of the server,
|
||||||
|
// for logging purposes
|
||||||
|
<-gotConn.Wait()
|
||||||
|
|
||||||
|
lazyDownload := &LazyReader{
|
||||||
|
CreateReader: func() (io.ReadCloser, error) {
|
||||||
|
<-gotDownResponse.Wait()
|
||||||
|
if downResponse == nil {
|
||||||
|
return nil, errors.New("downResponse failed")
|
||||||
|
}
|
||||||
|
return downResponse, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return lazyDownload, remoteAddr, localAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Header = c.transportConfig.GetRequestHeader()
|
||||||
|
|
||||||
|
if c.isH2 || c.isH3 {
|
||||||
|
resp, err := c.upload.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return errors.New("bad status code:", resp.Status)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// stringify the entire HTTP/1.1 request so it can be
|
||||||
|
// safely retried. if instead req.Write is called multiple
|
||||||
|
// times, the body is already drained after the first
|
||||||
|
// request
|
||||||
|
requestBytes := new(bytes.Buffer)
|
||||||
|
common.Must(req.Write(requestBytes))
|
||||||
|
|
||||||
|
var uploadConn any
|
||||||
|
|
||||||
|
for {
|
||||||
|
uploadConn = c.uploadRawPool.Get()
|
||||||
|
newConnection := uploadConn == nil
|
||||||
|
if newConnection {
|
||||||
|
uploadConn, err = c.dialUploadConn(context.WithoutCancel(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = uploadConn.(net.Conn).Write(requestBytes.Bytes())
|
||||||
|
|
||||||
|
// if the write failed, we try another connection from
|
||||||
|
// the pool, until the write on a new connection fails.
|
||||||
|
// failed writes to a pooled connection are normal when
|
||||||
|
// the connection has been closed in the meantime.
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
} else if newConnection {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.uploadRawPool.Put(uploadConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@@ -1,26 +1,25 @@
|
|||||||
package splithttp
|
package splithttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
gonet "net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptrace"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/signal/done"
|
|
||||||
"github.com/xtls/xray-core/common/signal/semaphore"
|
"github.com/xtls/xray-core/common/signal/semaphore"
|
||||||
"github.com/xtls/xray-core/common/uuid"
|
"github.com/xtls/xray-core/common/uuid"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/browser_dialer"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
"github.com/xtls/xray-core/transport/pipe"
|
"github.com/xtls/xray-core/transport/pipe"
|
||||||
@@ -32,26 +31,21 @@ type dialerConf struct {
|
|||||||
*internet.MemoryStreamConfig
|
*internet.MemoryStreamConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type reusedClient struct {
|
|
||||||
download *http.Client
|
|
||||||
upload *http.Client
|
|
||||||
isH2 bool
|
|
||||||
// pool of net.Conn, created using dialUploadConn
|
|
||||||
uploadRawPool *sync.Pool
|
|
||||||
dialUploadConn func(ctxInner context.Context) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
globalDialerMap map[dialerConf]reusedClient
|
globalDialerMap map[dialerConf]DialerClient
|
||||||
globalDialerAccess sync.Mutex
|
globalDialerAccess sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) reusedClient {
|
func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient {
|
||||||
|
if browser_dialer.HasBrowserDialer() {
|
||||||
|
return &BrowserDialerClient{}
|
||||||
|
}
|
||||||
|
|
||||||
globalDialerAccess.Lock()
|
globalDialerAccess.Lock()
|
||||||
defer globalDialerAccess.Unlock()
|
defer globalDialerAccess.Unlock()
|
||||||
|
|
||||||
if globalDialerMap == nil {
|
if globalDialerMap == nil {
|
||||||
globalDialerMap = make(map[dialerConf]reusedClient)
|
globalDialerMap = make(map[dialerConf]DialerClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found {
|
if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found {
|
||||||
@@ -60,6 +54,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
|
|
||||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
|
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
|
||||||
|
isH3 := tlsConfig != nil && (len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3")
|
||||||
|
|
||||||
var gotlsConfig *gotls.Config
|
var gotlsConfig *gotls.Config
|
||||||
|
|
||||||
@@ -87,10 +82,56 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var uploadTransport http.RoundTripper
|
|
||||||
var downloadTransport http.RoundTripper
|
var downloadTransport http.RoundTripper
|
||||||
|
var uploadTransport http.RoundTripper
|
||||||
|
|
||||||
if isH2 {
|
if isH3 {
|
||||||
|
dest.Network = net.Network_UDP
|
||||||
|
quicConfig := &quic.Config{
|
||||||
|
HandshakeIdleTimeout: 10 * time.Second,
|
||||||
|
MaxIdleTimeout: 90 * time.Second,
|
||||||
|
KeepAlivePeriod: 3 * time.Second,
|
||||||
|
Allow0RTT: true,
|
||||||
|
}
|
||||||
|
roundTripper := &http3.RoundTripper{
|
||||||
|
TLSClientConfig: gotlsConfig,
|
||||||
|
QUICConfig: quicConfig,
|
||||||
|
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{
|
downloadTransport = &http2.Transport{
|
||||||
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
|
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
|
||||||
return dialContext(ctxInner)
|
return dialContext(ctxInner)
|
||||||
@@ -111,12 +152,12 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
// http.Client and our custom dial context.
|
// http.Client and our custom dial context.
|
||||||
DisableKeepAlives: true,
|
DisableKeepAlives: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// we use uploadRawPool for that
|
// we use uploadRawPool for that
|
||||||
uploadTransport = nil
|
uploadTransport = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
client := reusedClient{
|
client := &DefaultDialerClient{
|
||||||
|
transportConfig: streamSettings.ProtocolSettings.(*Config),
|
||||||
download: &http.Client{
|
download: &http.Client{
|
||||||
Transport: downloadTransport,
|
Transport: downloadTransport,
|
||||||
},
|
},
|
||||||
@@ -124,6 +165,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
Transport: uploadTransport,
|
Transport: uploadTransport,
|
||||||
},
|
},
|
||||||
isH2: isH2,
|
isH2: isH2,
|
||||||
|
isH3: isH3,
|
||||||
uploadRawPool: &sync.Pool{},
|
uploadRawPool: &sync.Pool{},
|
||||||
dialUploadConn: dialContext,
|
dialUploadConn: dialContext,
|
||||||
}
|
}
|
||||||
@@ -160,80 +202,9 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
|
|
||||||
httpClient := getHTTPClient(ctx, dest, streamSettings)
|
httpClient := getHTTPClient(ctx, dest, streamSettings)
|
||||||
|
|
||||||
var remoteAddr gonet.Addr
|
|
||||||
var localAddr gonet.Addr
|
|
||||||
// this is done when the TCP/UDP connection to the server was established,
|
|
||||||
// and we can unblock the Dial function and print correct net addresses in
|
|
||||||
// logs
|
|
||||||
gotConn := done.New()
|
|
||||||
|
|
||||||
var downResponse io.ReadCloser
|
|
||||||
gotDownResponse := done.New()
|
|
||||||
|
|
||||||
sessionIdUuid := uuid.New()
|
sessionIdUuid := uuid.New()
|
||||||
sessionId := sessionIdUuid.String()
|
sessionId := sessionIdUuid.String()
|
||||||
|
baseURL := requestURL.String() + sessionId
|
||||||
go func() {
|
|
||||||
trace := &httptrace.ClientTrace{
|
|
||||||
GotConn: func(connInfo httptrace.GotConnInfo) {
|
|
||||||
remoteAddr = connInfo.Conn.RemoteAddr()
|
|
||||||
localAddr = connInfo.Conn.LocalAddr()
|
|
||||||
gotConn.Close()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case we hit an error, we want to unblock this part
|
|
||||||
defer gotConn.Close()
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(
|
|
||||||
httptrace.WithClientTrace(context.WithoutCancel(ctx), trace),
|
|
||||||
"GET",
|
|
||||||
requestURL.String()+sessionId,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogInfoInner(ctx, err, "failed to construct download http request")
|
|
||||||
gotDownResponse.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header = transportConfiguration.GetRequestHeader()
|
|
||||||
|
|
||||||
response, err := httpClient.download.Do(req)
|
|
||||||
gotConn.Close()
|
|
||||||
if err != nil {
|
|
||||||
errors.LogInfoInner(ctx, err, "failed to send download http request")
|
|
||||||
gotDownResponse.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.StatusCode != 200 {
|
|
||||||
response.Body.Close()
|
|
||||||
errors.LogInfo(ctx, "invalid status code on download:", response.Status)
|
|
||||||
gotDownResponse.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip "ooooooooook" response
|
|
||||||
trashHeader := []byte{0}
|
|
||||||
for {
|
|
||||||
_, err = io.ReadFull(response.Body, trashHeader)
|
|
||||||
if err != nil {
|
|
||||||
response.Body.Close()
|
|
||||||
errors.LogInfoInner(ctx, err, "failed to read initial response")
|
|
||||||
gotDownResponse.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if trashHeader[0] == 'k' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
downResponse = response.Body
|
|
||||||
gotDownResponse.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
uploadUrl := requestURL.String() + sessionId + "/"
|
|
||||||
|
|
||||||
uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize))
|
uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize))
|
||||||
|
|
||||||
@@ -252,97 +223,55 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
|
|
||||||
<-requestsLimiter.Wait()
|
<-requestsLimiter.Wait()
|
||||||
|
|
||||||
url := uploadUrl + strconv.FormatInt(requestCounter, 10)
|
seq := requestCounter
|
||||||
requestCounter += 1
|
requestCounter += 1
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer requestsLimiter.Signal()
|
defer requestsLimiter.Signal()
|
||||||
req, err := http.NewRequest("POST", url, &buf.MultiBufferContainer{MultiBuffer: chunk})
|
|
||||||
|
err := httpClient.SendUploadRequest(
|
||||||
|
context.WithoutCancel(ctx),
|
||||||
|
baseURL+"/"+strconv.FormatInt(seq, 10),
|
||||||
|
&buf.MultiBufferContainer{MultiBuffer: chunk},
|
||||||
|
int64(chunk.Len()),
|
||||||
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogInfoInner(ctx, err, "failed to send upload")
|
errors.LogInfoInner(ctx, err, "failed to send upload")
|
||||||
uploadPipeReader.Interrupt()
|
uploadPipeReader.Interrupt()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.ContentLength = int64(chunk.Len())
|
|
||||||
req.Header = transportConfiguration.GetRequestHeader()
|
|
||||||
|
|
||||||
if httpClient.isH2 {
|
|
||||||
resp, err := httpClient.upload.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogInfoInner(ctx, err, "failed to send upload")
|
|
||||||
uploadPipeReader.Interrupt()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
errors.LogInfo(ctx, "failed to send upload, bad status code:", resp.Status)
|
|
||||||
uploadPipeReader.Interrupt()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var uploadConn any
|
|
||||||
|
|
||||||
// stringify the entire HTTP/1.1 request so it can be
|
|
||||||
// safely retried. if instead req.Write is called multiple
|
|
||||||
// times, the body is already drained after the first
|
|
||||||
// request
|
|
||||||
requestBytes := new(bytes.Buffer)
|
|
||||||
common.Must(req.Write(requestBytes))
|
|
||||||
|
|
||||||
for {
|
|
||||||
uploadConn = httpClient.uploadRawPool.Get()
|
|
||||||
newConnection := uploadConn == nil
|
|
||||||
if newConnection {
|
|
||||||
uploadConn, err = httpClient.dialUploadConn(context.WithoutCancel(ctx))
|
|
||||||
if err != nil {
|
|
||||||
errors.LogInfoInner(ctx, err, "failed to connect upload")
|
|
||||||
uploadPipeReader.Interrupt()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = uploadConn.(net.Conn).Write(requestBytes.Bytes())
|
|
||||||
|
|
||||||
// if the write failed, we try another connection from
|
|
||||||
// the pool, until the write on a new connection fails.
|
|
||||||
// failed writes to a pooled connection are normal when
|
|
||||||
// the connection has been closed in the meantime.
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
} else if newConnection {
|
|
||||||
errors.LogInfoInner(ctx, err, "failed to send upload")
|
|
||||||
uploadPipeReader.Interrupt()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
httpClient.uploadRawPool.Put(uploadConn)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// we want to block Dial until we know the remote address of the server,
|
lazyRawDownload, remoteAddr, localAddr, err := httpClient.OpenDownload(context.WithoutCancel(ctx), baseURL)
|
||||||
// for logging purposes
|
if err != nil {
|
||||||
<-gotConn.Wait()
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lazyDownload := &LazyReader{
|
||||||
|
CreateReader: func() (io.ReadCloser, error) {
|
||||||
|
// skip "ooooooooook" response
|
||||||
|
trashHeader := []byte{0}
|
||||||
|
for {
|
||||||
|
_, err := io.ReadFull(lazyRawDownload, trashHeader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to read initial response").Base(err)
|
||||||
|
}
|
||||||
|
if trashHeader[0] == 'k' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lazyRawDownload, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// necessary in order to send larger chunks in upload
|
// necessary in order to send larger chunks in upload
|
||||||
bufferedUploadPipeWriter := buf.NewBufferedWriter(uploadPipeWriter)
|
bufferedUploadPipeWriter := buf.NewBufferedWriter(uploadPipeWriter)
|
||||||
bufferedUploadPipeWriter.SetBuffered(false)
|
bufferedUploadPipeWriter.SetBuffered(false)
|
||||||
|
|
||||||
lazyDownload := &LazyReader{
|
|
||||||
CreateReader: func() (io.ReadCloser, error) {
|
|
||||||
<-gotDownResponse.Wait()
|
|
||||||
if downResponse == nil {
|
|
||||||
return nil, errors.New("downResponse failed")
|
|
||||||
}
|
|
||||||
return downResponse, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := splitConn{
|
conn := splitConn{
|
||||||
writer: bufferedUploadPipeWriter,
|
writer: bufferedUploadPipeWriter,
|
||||||
reader: lazyDownload,
|
reader: lazyDownload,
|
||||||
|
@@ -11,6 +11,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -27,12 +29,13 @@ type requestHandler struct {
|
|||||||
host string
|
host string
|
||||||
path string
|
path string
|
||||||
ln *Listener
|
ln *Listener
|
||||||
|
sessionMu *sync.Mutex
|
||||||
sessions sync.Map
|
sessions sync.Map
|
||||||
localAddr gonet.TCPAddr
|
localAddr gonet.TCPAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpSession struct {
|
type httpSession struct {
|
||||||
uploadQueue *UploadQueue
|
uploadQueue *uploadQueue
|
||||||
// for as long as the GET request is not opened by the client, this will be
|
// for as long as the GET request is not opened by the client, this will be
|
||||||
// open ("undone"), and the session may be expired within a certain TTL.
|
// open ("undone"), and the session may be expired within a certain TTL.
|
||||||
// after the client connects, this becomes "done" and the session lives as
|
// after the client connects, this becomes "done" and the session lives as
|
||||||
@@ -56,11 +59,21 @@ func (h *requestHandler) maybeReapSession(isFullyConnected *done.Instance, sessi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
||||||
|
// fast path
|
||||||
currentSessionAny, ok := h.sessions.Load(sessionId)
|
currentSessionAny, ok := h.sessions.Load(sessionId)
|
||||||
if ok {
|
if ok {
|
||||||
return currentSessionAny.(*httpSession)
|
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{
|
s := &httpSession{
|
||||||
uploadQueue: NewUploadQueue(int(2 * h.ln.config.GetNormalizedMaxConcurrentUploads())),
|
uploadQueue: NewUploadQueue(int(2 * h.ln.config.GetNormalizedMaxConcurrentUploads())),
|
||||||
isFullyConnected: done.New(),
|
isFullyConnected: done.New(),
|
||||||
@@ -72,7 +85,7 @@ func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
if len(h.host) > 0 && request.Host != h.host {
|
if len(h.host) > 0 && !internet.IsValidHTTPHost(request.Host, h.host) {
|
||||||
errors.LogInfo(context.Background(), "failed to validate host, request:", request.Host, ", config:", h.host)
|
errors.LogInfo(context.Background(), "failed to validate host, request:", request.Host, ", config:", h.host)
|
||||||
writer.WriteHeader(http.StatusNotFound)
|
writer.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
@@ -163,7 +176,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
writer.Header().Set("X-Accel-Buffering", "no")
|
writer.Header().Set("X-Accel-Buffering", "no")
|
||||||
// magic header to make the HTTP middle box consider this as SSE to disable buffer
|
// magic header to make the HTTP middle box consider this as SSE to disable buffer
|
||||||
writer.Header().Set("Content-Type", "text/event-stream")
|
writer.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
|
||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
// send a chunk immediately to enable CDN streaming.
|
// send a chunk immediately to enable CDN streaming.
|
||||||
// many CDN buffer the response headers until the origin starts sending
|
// many CDN buffer the response headers until the origin starts sending
|
||||||
@@ -222,10 +235,13 @@ func (c *httpResponseBodyWriter) Close() error {
|
|||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
server http.Server
|
server http.Server
|
||||||
listener net.Listener
|
h3server *http3.Server
|
||||||
config *Config
|
listener net.Listener
|
||||||
addConn internet.ConnHandler
|
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) {
|
func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
|
||||||
@@ -242,6 +258,17 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
var err error
|
var err error
|
||||||
var localAddr = gonet.TCPAddr{}
|
var localAddr = gonet.TCPAddr{}
|
||||||
|
handler := &requestHandler{
|
||||||
|
host: shSettings.Host,
|
||||||
|
path: shSettings.GetNormalizedPath(),
|
||||||
|
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
|
if port == net.Port(0) { // unix
|
||||||
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
||||||
@@ -252,6 +279,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)
|
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)
|
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
|
} else { // tcp
|
||||||
localAddr = gonet.TCPAddr{
|
localAddr = gonet.TCPAddr{
|
||||||
IP: address.IP(),
|
IP: address.IP(),
|
||||||
@@ -264,40 +314,29 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to listen TCP(for SH) on ", address, ":", port).Base(err)
|
return nil, errors.New("failed to listen TCP(for SH) on ", address, ":", port).Base(err)
|
||||||
}
|
}
|
||||||
|
l.listener = listener
|
||||||
errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port)
|
errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port)
|
||||||
}
|
|
||||||
|
|
||||||
|
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
||||||
|
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
l.listener = listener
|
||||||
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||||
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
|
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
|
||||||
listener = tls.NewListener(listener, tlsConfig)
|
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")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return l, err
|
return l, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,9 +347,22 @@ func (ln *Listener) Addr() net.Addr {
|
|||||||
|
|
||||||
// Close implements net.Listener.Close().
|
// Close implements net.Listener.Close().
|
||||||
func (ln *Listener) Close() error {
|
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() {
|
func init() {
|
||||||
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
|
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||||
"github.com/xtls/xray-core/testing/servers/tcp"
|
"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"
|
||||||
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
@@ -63,8 +64,8 @@ func Test_listenSHAndDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
common.Must(conn.Close())
|
common.Must(conn.Close())
|
||||||
<-time.After(time.Second * 5)
|
|
||||||
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
|
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
_, err = conn.Write([]byte("Test connection 2"))
|
_, err = conn.Write([]byte("Test connection 2"))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
@@ -96,7 +97,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.Write([]byte("Response"))
|
_, err = c.Write([]byte(c.RemoteAddr().String()))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
}(conn)
|
}(conn)
|
||||||
})
|
})
|
||||||
@@ -113,7 +114,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
|||||||
|
|
||||||
var b [1024]byte
|
var b [1024]byte
|
||||||
n, _ := conn.Read(b[:])
|
n, _ := conn.Read(b[:])
|
||||||
if string(b[:n]) != "Response" {
|
if string(b[:n]) != "1.1.1.1:0" {
|
||||||
t.Error("response: ", string(b[:n]))
|
t.Error("response: ", string(b[:n]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,3 +205,42 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
|||||||
t.Error("Expected h2 but got:", resp.ProtoMajor)
|
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() {
|
||||||
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
defer listen.Close()
|
||||||
|
|
||||||
|
conn, err := Dial(context.Background(), net.UDPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
|
common.Must(err)
|
||||||
|
_ = conn.Close()
|
||||||
|
|
||||||
|
end := time.Now()
|
||||||
|
if !end.Before(start.Add(time.Second * 5)) {
|
||||||
|
t.Error("end: ", end, " start: ", start)
|
||||||
|
}
|
||||||
|
}
|
@@ -15,7 +15,7 @@ type Packet struct {
|
|||||||
Seq uint64
|
Seq uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type UploadQueue struct {
|
type uploadQueue struct {
|
||||||
pushedPackets chan Packet
|
pushedPackets chan Packet
|
||||||
heap uploadHeap
|
heap uploadHeap
|
||||||
nextSeq uint64
|
nextSeq uint64
|
||||||
@@ -23,8 +23,8 @@ type UploadQueue struct {
|
|||||||
maxPackets int
|
maxPackets int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUploadQueue(maxPackets int) *UploadQueue {
|
func NewUploadQueue(maxPackets int) *uploadQueue {
|
||||||
return &UploadQueue{
|
return &uploadQueue{
|
||||||
pushedPackets: make(chan Packet, maxPackets),
|
pushedPackets: make(chan Packet, maxPackets),
|
||||||
heap: uploadHeap{},
|
heap: uploadHeap{},
|
||||||
nextSeq: 0,
|
nextSeq: 0,
|
||||||
@@ -33,7 +33,7 @@ func NewUploadQueue(maxPackets int) *UploadQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *UploadQueue) Push(p Packet) error {
|
func (h *uploadQueue) Push(p Packet) error {
|
||||||
if h.closed {
|
if h.closed {
|
||||||
return errors.New("splithttp packet queue closed")
|
return errors.New("splithttp packet queue closed")
|
||||||
}
|
}
|
||||||
@@ -42,13 +42,13 @@ func (h *UploadQueue) Push(p Packet) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *UploadQueue) Close() error {
|
func (h *uploadQueue) Close() error {
|
||||||
h.closed = true
|
h.closed = true
|
||||||
close(h.pushedPackets)
|
close(h.pushedPackets)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *UploadQueue) Read(b []byte) (int, error) {
|
func (h *uploadQueue) Read(b []byte) (int, error) {
|
||||||
if h.closed {
|
if h.closed {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,7 @@ func ListenUnix(ctx context.Context, address net.Address, settings *MemoryStream
|
|||||||
protocol := settings.ProtocolName
|
protocol := settings.ProtocolName
|
||||||
listenFunc := transportListenerCache[protocol]
|
listenFunc := transportListenerCache[protocol]
|
||||||
if listenFunc == nil {
|
if listenFunc == nil {
|
||||||
return nil, errors.New(protocol, " unix istener not registered.").AtError()
|
return nil, errors.New(protocol, " unix listener not registered.").AtError()
|
||||||
}
|
}
|
||||||
listener, err := listenFunc(ctx, address, net.Port(0), settings, handler)
|
listener, err := listenFunc(ctx, address, net.Port(0), settings, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -14,13 +14,15 @@ import (
|
|||||||
var _ buf.Writer = (*connection)(nil)
|
var _ buf.Writer = (*connection)(nil)
|
||||||
|
|
||||||
// connection is a wrapper for net.Conn over WebSocket connection.
|
// 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 {
|
type connection struct {
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
remoteAddr net.Addr
|
remoteAddr net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConnection(conn *websocket.Conn, remoteAddr net.Addr, extraReader io.Reader) *connection {
|
func NewConnection(conn *websocket.Conn, remoteAddr net.Addr, extraReader io.Reader) *connection {
|
||||||
return &connection{
|
return &connection{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
remoteAddr: remoteAddr,
|
remoteAddr: remoteAddr,
|
||||||
|
@@ -1,54 +1,23 @@
|
|||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
gonet "net"
|
gonet "net"
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/platform"
|
|
||||||
"github.com/xtls/xray-core/common/uuid"
|
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/browser_dialer"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed dialer.html
|
|
||||||
var webpage []byte
|
|
||||||
|
|
||||||
var conns chan *websocket.Conn
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addr := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" })
|
|
||||||
if addr != "" {
|
|
||||||
token := uuid.New()
|
|
||||||
csrfToken := token.String()
|
|
||||||
webpage = bytes.ReplaceAll(webpage, []byte("csrfToken"), []byte(csrfToken))
|
|
||||||
conns = make(chan *websocket.Conn, 256)
|
|
||||||
go http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.URL.Path == "/websocket" {
|
|
||||||
if r.URL.Query().Get("token") == csrfToken {
|
|
||||||
if conn, err := upgrader.Upgrade(w, r, nil); err == nil {
|
|
||||||
conns <- conn
|
|
||||||
} else {
|
|
||||||
errors.LogError(context.Background(), "Browser dialer http upgrade unexpected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w.Write(webpage)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial dials a WebSocket connection to the given destination.
|
// Dial dials a WebSocket connection to the given destination.
|
||||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
||||||
errors.LogInfo(ctx, "creating connection to ", dest)
|
errors.LogInfo(ctx, "creating connection to ", dest)
|
||||||
@@ -98,18 +67,18 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
// Like the NetDial in the dialer
|
// Like the NetDial in the dialer
|
||||||
pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
|
pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogErrorInner(ctx, err, "failed to dial to " + addr)
|
errors.LogErrorInner(ctx, err, "failed to dial to "+addr)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// TLS and apply the handshake
|
// TLS and apply the handshake
|
||||||
cn := tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn)
|
cn := tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn)
|
||||||
if err := cn.WebsocketHandshakeContext(ctx); err != nil {
|
if err := cn.WebsocketHandshakeContext(ctx); err != nil {
|
||||||
errors.LogErrorInner(ctx, err, "failed to dial to " + addr)
|
errors.LogErrorInner(ctx, err, "failed to dial to "+addr)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !tlsConfig.InsecureSkipVerify {
|
if !tlsConfig.InsecureSkipVerify {
|
||||||
if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil {
|
if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil {
|
||||||
errors.LogErrorInner(ctx, err, "failed to dial to " + addr)
|
errors.LogErrorInner(ctx, err, "failed to dial to "+addr)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,28 +93,13 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
}
|
}
|
||||||
uri := protocol + "://" + host + wsSettings.GetNormalizedPath()
|
uri := protocol + "://" + host + wsSettings.GetNormalizedPath()
|
||||||
|
|
||||||
if conns != nil {
|
if browser_dialer.HasBrowserDialer() {
|
||||||
data := []byte(uri)
|
conn, err := browser_dialer.DialWS(uri, ed)
|
||||||
if ed != nil {
|
if err != nil {
|
||||||
data = append(data, " "+base64.RawURLEncoding.EncodeToString(ed)...)
|
|
||||||
}
|
|
||||||
var conn *websocket.Conn
|
|
||||||
for {
|
|
||||||
conn = <-conns
|
|
||||||
if conn.WriteMessage(websocket.TextMessage, data) != nil {
|
|
||||||
conn.Close()
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, p, err := conn.ReadMessage(); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if s := string(p); s != "ok" {
|
|
||||||
conn.Close()
|
|
||||||
return nil, errors.New(s)
|
|
||||||
}
|
}
|
||||||
return newConnection(conn, conn.RemoteAddr(), nil), nil
|
|
||||||
|
return NewConnection(conn, conn.RemoteAddr(), nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
header := wsSettings.GetRequestHeader()
|
header := wsSettings.GetRequestHeader()
|
||||||
@@ -163,7 +117,7 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
return nil, errors.New("failed to dial to (", uri, "): ", reason).Base(err)
|
return nil, errors.New("failed to dial to (", uri, "): ", reason).Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newConnection(conn, conn.RemoteAddr(), nil), nil
|
return NewConnection(conn, conn.RemoteAddr(), nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type delayDialConn struct {
|
type delayDialConn struct {
|
||||||
|
@@ -1,59 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Browser Dialer</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script>
|
|
||||||
// Copyright (c) 2021 XRAY. Mozilla Public License 2.0.
|
|
||||||
var url = "ws://" + window.location.host + "/websocket?token=csrfToken"
|
|
||||||
var count = 0
|
|
||||||
setInterval(check, 1000)
|
|
||||||
function check() {
|
|
||||||
if (count <= 0) {
|
|
||||||
count += 1
|
|
||||||
console.log("Prepare", url)
|
|
||||||
var ws = new WebSocket(url)
|
|
||||||
// arraybuffer is significantly faster in chrome than default
|
|
||||||
// blob, tested with chrome 123
|
|
||||||
ws.binaryType = "arraybuffer";
|
|
||||||
var wss = undefined
|
|
||||||
var first = true
|
|
||||||
ws.onmessage = function (event) {
|
|
||||||
if (first) {
|
|
||||||
first = false
|
|
||||||
count -= 1
|
|
||||||
var arr = event.data.split(" ")
|
|
||||||
console.log("Dial", arr[0], arr[1])
|
|
||||||
wss = new WebSocket(arr[0], arr[1])
|
|
||||||
wss.binaryType = "arraybuffer";
|
|
||||||
var opened = false
|
|
||||||
wss.onopen = function (event) {
|
|
||||||
opened = true
|
|
||||||
ws.send("ok")
|
|
||||||
}
|
|
||||||
wss.onmessage = function (event) {
|
|
||||||
ws.send(event.data)
|
|
||||||
}
|
|
||||||
wss.onclose = function (event) {
|
|
||||||
ws.close()
|
|
||||||
}
|
|
||||||
wss.onerror = function (event) {
|
|
||||||
!opened && ws.send("fail")
|
|
||||||
wss.close()
|
|
||||||
}
|
|
||||||
check()
|
|
||||||
} else wss.send(event.data)
|
|
||||||
}
|
|
||||||
ws.onclose = function (event) {
|
|
||||||
if (first) count -= 1
|
|
||||||
else wss.close()
|
|
||||||
}
|
|
||||||
ws.onerror = function (event) {
|
|
||||||
ws.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -38,7 +38,7 @@ var upgrader = &websocket.Upgrader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
if len(h.host) > 0 && request.Host != h.host {
|
if len(h.host) > 0 && !internet.IsValidHTTPHost(request.Host, h.host) {
|
||||||
errors.LogInfo(context.Background(), "failed to validate host, request:", request.Host, ", config:", h.host)
|
errors.LogInfo(context.Background(), "failed to validate host, request:", request.Host, ", config:", h.host)
|
||||||
writer.WriteHeader(http.StatusNotFound)
|
writer.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
@@ -73,7 +73,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h.ln.addConn(newConnection(conn, remoteAddr, extraReader))
|
h.ln.addConn(NewConnection(conn, remoteAddr, extraReader))
|
||||||
}
|
}
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
|
@@ -91,7 +91,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.Write([]byte("Response"))
|
_, err = c.Write([]byte(c.RemoteAddr().String()))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
}(conn)
|
}(conn)
|
||||||
})
|
})
|
||||||
@@ -109,7 +109,7 @@ func TestDialWithRemoteAddr(t *testing.T) {
|
|||||||
var b [1024]byte
|
var b [1024]byte
|
||||||
n, err := conn.Read(b[:])
|
n, err := conn.Read(b[:])
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if string(b[:n]) != "Response" {
|
if string(b[:n]) != "1.1.1.1:0" {
|
||||||
t.Error("response: ", string(b[:n]))
|
t.Error("response: ", string(b[:n]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user