mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-06 13:58:04 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b53ae0a183 | ||
|
|
d2758a023c | ||
|
|
8aacdbd71b | ||
|
|
14524cc3b7 | ||
|
|
cb7bfeb54c | ||
|
|
d62f5cfb62 | ||
|
|
755f0a1d12 | ||
|
|
d8a8629a14 | ||
|
|
982c95d89a | ||
|
|
ae3ddd1c06 | ||
|
|
f926ee4aa0 | ||
|
|
67a71adad1 | ||
|
|
ce66db7032 | ||
|
|
7d93062f3d | ||
|
|
2320416ca3 |
@@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Update Geodat
|
- name: Update Geodat
|
||||||
id: update
|
id: update
|
||||||
uses: nick-fields/retry@v3
|
uses: nick-fields/retry@v4
|
||||||
with:
|
with:
|
||||||
timeout_minutes: 60
|
timeout_minutes: 60
|
||||||
retry_wait_seconds: 60
|
retry_wait_seconds: 60
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Update Wintun
|
- name: Update Wintun
|
||||||
id: update
|
id: update
|
||||||
uses: nick-fields/retry@v3
|
uses: nick-fields/retry@v4
|
||||||
with:
|
with:
|
||||||
timeout_minutes: 60
|
timeout_minutes: 60
|
||||||
retry_wait_seconds: 60
|
retry_wait_seconds: 60
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
- [Marzban](https://github.com/Gozargah/Marzban)
|
- [Marzban](https://github.com/Gozargah/Marzban)
|
||||||
- [Hiddify](https://github.com/hiddify/Hiddify-Manager)
|
- [Hiddify](https://github.com/hiddify/Hiddify-Manager)
|
||||||
- [TX-UI](https://github.com/AghayeCoder/tx-ui)
|
- [TX-UI](https://github.com/AghayeCoder/tx-ui)
|
||||||
|
- [CELERITY](https://github.com/ClickDevTech/CELERITY-panel)
|
||||||
- One Click
|
- One Click
|
||||||
- [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz)
|
- [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz)
|
||||||
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool), [VPainLess](https://github.com/vpainless/vpainless)
|
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool), [VPainLess](https://github.com/vpainless/vpainless)
|
||||||
@@ -122,6 +123,7 @@
|
|||||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973) | [Happ tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274)
|
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973) | [Happ tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274)
|
||||||
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
||||||
- [OneXray](https://github.com/OneXray/OneXray)
|
- [OneXray](https://github.com/OneXray/OneXray)
|
||||||
|
- [INCY](https://apps.apple.com/en/app/incy/id6756943388)
|
||||||
- macOS arm64 & x64
|
- macOS arm64 & x64
|
||||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973)
|
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973)
|
||||||
- [V2rayU](https://github.com/yanue/V2rayU)
|
- [V2rayU](https://github.com/yanue/V2rayU)
|
||||||
@@ -132,6 +134,7 @@
|
|||||||
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||||
- [GenyConnect](https://github.com/genyleap/GenyConnect)
|
- [GenyConnect](https://github.com/genyleap/GenyConnect)
|
||||||
|
- [INCY](https://apps.apple.com/en/app/incy/id6756943388)
|
||||||
- Linux
|
- Linux
|
||||||
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
||||||
- [Furious](https://github.com/LorenEteval/Furious)
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !windows && !wasm && !illumos
|
//go:build !windows && !wasm && !illumos && !openbsd
|
||||||
// +build !windows,!wasm,!illumos
|
// +build !windows,!wasm,!illumos,!openbsd
|
||||||
|
|
||||||
package buf
|
package buf
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !wasm
|
//go:build !wasm && !openbsd
|
||||||
// +build !wasm
|
// +build !wasm,!openbsd
|
||||||
|
|
||||||
package buf
|
package buf
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//go:build wasm || openbsd
|
||||||
|
// +build wasm openbsd
|
||||||
|
|
||||||
|
package buf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/features/stats"
|
||||||
|
)
|
||||||
|
|
||||||
|
const useReadv = false
|
||||||
|
|
||||||
|
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn, counter stats.Counter) Reader {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//go:build wasm
|
|
||||||
// +build wasm
|
|
||||||
|
|
||||||
package buf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const useReadv = false
|
|
||||||
|
|
||||||
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) Reader {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !wasm
|
//go:build !wasm && !openbsd
|
||||||
// +build !wasm
|
// +build !wasm,!openbsd
|
||||||
|
|
||||||
package buf_test
|
package buf_test
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ type serverityLogger struct {
|
|||||||
func NewLogger(logWriterCreator WriterCreator) Handler {
|
func NewLogger(logWriterCreator WriterCreator) Handler {
|
||||||
return &generalLogger{
|
return &generalLogger{
|
||||||
creator: logWriterCreator,
|
creator: logWriterCreator,
|
||||||
buffer: make(chan Message, 16),
|
buffer: make(chan Message, 128),
|
||||||
access: semaphore.New(1),
|
access: semaphore.New(1),
|
||||||
done: done.New(),
|
done: done.New(),
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ func ReplaceWithSeverityLogger(serverity Severity) {
|
|||||||
w := CreateStdoutLogWriter()
|
w := CreateStdoutLogWriter()
|
||||||
g := &generalLogger{
|
g := &generalLogger{
|
||||||
creator: w,
|
creator: w,
|
||||||
buffer: make(chan Message, 16),
|
buffer: make(chan Message, 128),
|
||||||
access: semaphore.New(1),
|
access: semaphore.New(1),
|
||||||
done: done.New(),
|
done: done.New(),
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ func GetGlobalID(ctx context.Context) (globalID [8]byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.Network == net.Network_UDP &&
|
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.Network == net.Network_UDP &&
|
||||||
(inbound.Name == "dokodemo-door" || inbound.Name == "socks" || inbound.Name == "shadowsocks" || inbound.Name == "tun") {
|
(inbound.Name == "dokodemo-door" || inbound.Name == "socks" || inbound.Name == "shadowsocks" || inbound.Name == "tun" || inbound.Name == "wireguard") {
|
||||||
h := blake3.New(8, BaseKey)
|
h := blake3.New(8, BaseKey)
|
||||||
h.Write([]byte(inbound.Source.String()))
|
h.Write([]byte(inbound.Source.String()))
|
||||||
copy(globalID[:], h.Sum(nil))
|
copy(globalID[:], h.Sum(nil))
|
||||||
|
|||||||
+2
-2
@@ -19,8 +19,8 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Version_x byte = 26
|
Version_x byte = 26
|
||||||
Version_y byte = 2
|
Version_y byte = 3
|
||||||
Version_z byte = 6
|
Version_z byte = 27
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ require (
|
|||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535
|
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.49.0
|
golang.org/x/crypto v0.49.0
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||||
@@ -26,7 +26,7 @@ require (
|
|||||||
golang.org/x/sync v0.20.0
|
golang.org/x/sync v0.20.0
|
||||||
golang.org/x/sys v0.42.0
|
golang.org/x/sys v0.42.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||||
google.golang.org/grpc v1.79.3
|
google.golang.org/grpc v1.79.3
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
|||||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 h1:nwobseOLLRtdbP6z7Z2aVI97u8ZptTgD1ofovhAKmeU=
|
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f h1:iy2JRioxmUpoJ3SzbFPyTxHZMbR/rSHP7dOOgYaq1O8=
|
||||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
|
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f/go.mod h1:DsJblcWDGt76+FVqBVwbwRhxyyNJsGV48gJLch0OOWI=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
@@ -129,8 +129,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ type SplitHTTPConfig struct {
|
|||||||
ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"`
|
ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"`
|
||||||
ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"`
|
ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"`
|
||||||
ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"`
|
ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"`
|
||||||
|
AllowH2C bool `json:"allowH2C"`
|
||||||
Xmux XmuxConfig `json:"xmux"`
|
Xmux XmuxConfig `json:"xmux"`
|
||||||
DownloadSettings *StreamConfig `json:"downloadSettings"`
|
DownloadSettings *StreamConfig `json:"downloadSettings"`
|
||||||
Extra json.RawMessage `json:"extra"`
|
Extra json.RawMessage `json:"extra"`
|
||||||
@@ -426,6 +427,7 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
|||||||
ScMaxBufferedPosts: c.ScMaxBufferedPosts,
|
ScMaxBufferedPosts: c.ScMaxBufferedPosts,
|
||||||
ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs),
|
ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs),
|
||||||
ServerMaxHeaderBytes: c.ServerMaxHeaderBytes,
|
ServerMaxHeaderBytes: c.ServerMaxHeaderBytes,
|
||||||
|
AllowH2C: c.AllowH2C,
|
||||||
Xmux: &splithttp.XmuxConfig{
|
Xmux: &splithttp.XmuxConfig{
|
||||||
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
|
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
|
||||||
MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
|
MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
|
||||||
@@ -1425,10 +1427,11 @@ func (c *FragmentMask) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NoiseItem struct {
|
type NoiseItem struct {
|
||||||
Rand Int32Range `json:"rand"`
|
Rand Int32Range `json:"rand"`
|
||||||
Type string `json:"type"`
|
RandRange *Int32Range `json:"randRange"`
|
||||||
Packet json.RawMessage `json:"packet"`
|
Type string `json:"type"`
|
||||||
Delay Int32Range `json:"delay"`
|
Packet json.RawMessage `json:"packet"`
|
||||||
|
Delay Int32Range `json:"delay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NoiseMask struct {
|
type NoiseMask struct {
|
||||||
@@ -1445,16 +1448,24 @@ func (c *NoiseMask) Build() (proto.Message, error) {
|
|||||||
|
|
||||||
noiseSlice := make([]*noise.Item, 0, len(c.Noise))
|
noiseSlice := make([]*noise.Item, 0, len(c.Noise))
|
||||||
for _, item := range c.Noise {
|
for _, item := range c.Noise {
|
||||||
|
if item.RandRange == nil {
|
||||||
|
item.RandRange = &Int32Range{From: 0, To: 255}
|
||||||
|
}
|
||||||
|
if item.RandRange.From < 0 || item.RandRange.To > 255 {
|
||||||
|
return nil, errors.New("invalid randRange")
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
noiseSlice = append(noiseSlice, &noise.Item{
|
noiseSlice = append(noiseSlice, &noise.Item{
|
||||||
RandMin: int64(item.Rand.From),
|
RandMin: int64(item.Rand.From),
|
||||||
RandMax: int64(item.Rand.To),
|
RandMax: int64(item.Rand.To),
|
||||||
Packet: item.Packet,
|
RandRangeMin: item.RandRange.From,
|
||||||
DelayMin: int64(item.Delay.From),
|
RandRangeMax: item.RandRange.To,
|
||||||
DelayMax: int64(item.Delay.To),
|
Packet: item.Packet,
|
||||||
|
DelayMin: int64(item.Delay.From),
|
||||||
|
DelayMax: int64(item.Delay.To),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-8
@@ -78,8 +78,13 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
|
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Reverse != nil && account.Reverse.Tag == "" {
|
if account.Reverse != nil {
|
||||||
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
|
if account.Reverse.Tag == "" {
|
||||||
|
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
|
||||||
|
}
|
||||||
|
if account.Reverse.Sniffing != nil { // may not be reached: error json unmarshal
|
||||||
|
return nil, errors.New(`VLESS clients: inbound's "reverse" can't have "sniffing"`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Account = serial.ToTypedMessage(account)
|
user.Account = serial.ToTypedMessage(account)
|
||||||
@@ -197,6 +202,28 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VLessReverseConfig struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Sniffing *SniffingConfig `json:"sniffing"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VLessReverseConfig) Build() (*vless.Reverse, error) {
|
||||||
|
if c.Tag == "" {
|
||||||
|
return nil, errors.New(`VLESS reverse: "tag" can't be empty`)
|
||||||
|
}
|
||||||
|
r := &vless.Reverse{
|
||||||
|
Tag: c.Tag,
|
||||||
|
}
|
||||||
|
if c.Sniffing != nil {
|
||||||
|
sc, err := c.Sniffing.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(`VLESS reverse: invalid "sniffing" config`).Base(err)
|
||||||
|
}
|
||||||
|
r.Sniffing = sc
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
type VLessOutboundVnext struct {
|
type VLessOutboundVnext struct {
|
||||||
Address *Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
Port uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
@@ -212,7 +239,7 @@ type VLessOutboundConfig struct {
|
|||||||
Flow string `json:"flow"`
|
Flow string `json:"flow"`
|
||||||
Seed string `json:"seed"`
|
Seed string `json:"seed"`
|
||||||
Encryption string `json:"encryption"`
|
Encryption string `json:"encryption"`
|
||||||
Reverse *vless.Reverse `json:"reverse"`
|
Reverse *VLessReverseConfig `json:"reverse"`
|
||||||
Testpre uint32 `json:"testpre"`
|
Testpre uint32 `json:"testpre"`
|
||||||
Testseed []uint32 `json:"testseed"`
|
Testseed []uint32 `json:"testseed"`
|
||||||
Vnext []*VLessOutboundVnext `json:"vnext"`
|
Vnext []*VLessOutboundVnext `json:"vnext"`
|
||||||
@@ -260,13 +287,22 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
|||||||
account.Flow = c.Flow
|
account.Flow = c.Flow
|
||||||
//account.Seed = c.Seed
|
//account.Seed = c.Seed
|
||||||
account.Encryption = c.Encryption
|
account.Encryption = c.Encryption
|
||||||
account.Reverse = c.Reverse
|
if c.Reverse != nil {
|
||||||
|
rvs, err := c.Reverse.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
account.Reverse = rvs
|
||||||
|
}
|
||||||
account.Testpre = c.Testpre
|
account.Testpre = c.Testpre
|
||||||
account.Testseed = c.Testseed
|
account.Testseed = c.Testseed
|
||||||
} else {
|
} else {
|
||||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||||
return nil, errors.New(`VLESS users: invalid user`).Base(err)
|
return nil, errors.New(`VLESS users: invalid user`).Base(err)
|
||||||
}
|
}
|
||||||
|
if account.Reverse != nil { // may not be reached: error json unmarshal
|
||||||
|
return nil, errors.New(`VLESS users: please use simplified outbound's config style to use "reverse"`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := uuid.ParseString(account.Id)
|
u, err := uuid.ParseString(account.Id)
|
||||||
@@ -326,10 +362,6 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
|||||||
return nil, errors.New(`VLESS users: unsupported "encryption": ` + account.Encryption)
|
return nil, errors.New(`VLESS users: unsupported "encryption": ` + account.Encryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Reverse != nil && account.Reverse.Tag == "" {
|
|
||||||
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Account = serial.ToTypedMessage(account)
|
user.Account = serial.ToTypedMessage(account)
|
||||||
spec.User = user
|
spec.User = user
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func ParseWireGuardKey(str string) (string, error) {
|
|||||||
return "", errors.New("key must not be empty")
|
return "", errors.New("key must not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(str)%2 == 0 {
|
if len(str) == 64 {
|
||||||
_, err = hex.DecodeString(str)
|
_, err = hex.DecodeString(str)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return str, nil
|
return str, nil
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet
|
|||||||
ctx = session.ContextWithContent(ctx, content)
|
ctx = session.ContextWithContent(ctx, content)
|
||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
if inbound == nil {
|
||||||
|
inbound = &session.Inbound{}
|
||||||
|
}
|
||||||
|
|
||||||
inbound.Tag = l.config.InboundTag
|
inbound.Tag = l.config.InboundTag
|
||||||
|
|
||||||
|
|||||||
+37
-29
@@ -322,6 +322,7 @@ func NewVisionWriter(writer buf.Writer, trafficState *TrafficState, isUplink boo
|
|||||||
func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
var isPadding *bool
|
var isPadding *bool
|
||||||
var switchToDirectCopy *bool
|
var switchToDirectCopy *bool
|
||||||
|
var spliceReadyInbound *session.Inbound
|
||||||
if w.isUplink {
|
if w.isUplink {
|
||||||
isPadding = &w.trafficState.Outbound.IsPadding
|
isPadding = &w.trafficState.Outbound.IsPadding
|
||||||
switchToDirectCopy = &w.trafficState.Outbound.UplinkWriterDirectCopy
|
switchToDirectCopy = &w.trafficState.Outbound.UplinkWriterDirectCopy
|
||||||
@@ -333,7 +334,7 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
if *switchToDirectCopy {
|
if *switchToDirectCopy {
|
||||||
if inbound := session.InboundFromContext(w.ctx); inbound != nil {
|
if inbound := session.InboundFromContext(w.ctx); inbound != nil {
|
||||||
if !w.isUplink && inbound.CanSpliceCopy == 2 {
|
if !w.isUplink && inbound.CanSpliceCopy == 2 {
|
||||||
inbound.CanSpliceCopy = 1
|
spliceReadyInbound = inbound
|
||||||
}
|
}
|
||||||
// if w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 { // TODO: enable uplink splice
|
// if w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 { // TODO: enable uplink splice
|
||||||
// w.ob.CanSpliceCopy = 1
|
// w.ob.CanSpliceCopy = 1
|
||||||
@@ -355,43 +356,51 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
if *isPadding {
|
if *isPadding {
|
||||||
if len(mb) == 1 && mb[0] == nil {
|
if len(mb) == 1 && mb[0] == nil {
|
||||||
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx, w.testseed) // we do a long padding to hide vless header
|
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx, w.testseed) // we do a long padding to hide vless header
|
||||||
return w.Writer.WriteMultiBuffer(mb)
|
} else {
|
||||||
}
|
isComplete := IsCompleteRecord(mb)
|
||||||
isComplete := IsCompleteRecord(mb)
|
mb = ReshapeMultiBuffer(w.ctx, mb)
|
||||||
mb = ReshapeMultiBuffer(w.ctx, mb)
|
longPadding := w.trafficState.IsTLS
|
||||||
longPadding := w.trafficState.IsTLS
|
for i, b := range mb {
|
||||||
for i, b := range mb {
|
if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) && isComplete {
|
||||||
if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) && isComplete {
|
if w.trafficState.EnableXtls {
|
||||||
if w.trafficState.EnableXtls {
|
*switchToDirectCopy = true
|
||||||
*switchToDirectCopy = true
|
}
|
||||||
|
var command byte = CommandPaddingContinue
|
||||||
|
if i == len(mb)-1 {
|
||||||
|
command = CommandPaddingEnd
|
||||||
|
if w.trafficState.EnableXtls {
|
||||||
|
command = CommandPaddingDirect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx, w.testseed)
|
||||||
|
*isPadding = false // padding going to end
|
||||||
|
longPadding = false
|
||||||
|
continue
|
||||||
|
} else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
|
||||||
|
*isPadding = false
|
||||||
|
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx, w.testseed)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
var command byte = CommandPaddingContinue
|
var command byte = CommandPaddingContinue
|
||||||
if i == len(mb)-1 {
|
if i == len(mb)-1 && !*isPadding {
|
||||||
command = CommandPaddingEnd
|
command = CommandPaddingEnd
|
||||||
if w.trafficState.EnableXtls {
|
if w.trafficState.EnableXtls {
|
||||||
command = CommandPaddingDirect
|
command = CommandPaddingDirect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx, w.testseed)
|
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx, w.testseed)
|
||||||
*isPadding = false // padding going to end
|
|
||||||
longPadding = false
|
|
||||||
continue
|
|
||||||
} else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
|
|
||||||
*isPadding = false
|
|
||||||
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx, w.testseed)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
var command byte = CommandPaddingContinue
|
|
||||||
if i == len(mb)-1 && !*isPadding {
|
|
||||||
command = CommandPaddingEnd
|
|
||||||
if w.trafficState.EnableXtls {
|
|
||||||
command = CommandPaddingDirect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx, w.testseed)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return w.Writer.WriteMultiBuffer(mb)
|
if err := w.Writer.WriteMultiBuffer(mb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if spliceReadyInbound != nil && spliceReadyInbound.CanSpliceCopy == 2 {
|
||||||
|
// Enable splice only after this write has completed to avoid racing
|
||||||
|
// concurrent direct writes to the same TCP connection.
|
||||||
|
spliceReadyInbound.CanSpliceCopy = 1
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCompleteRecord Is complete tls data record
|
// IsCompleteRecord Is complete tls data record
|
||||||
@@ -744,7 +753,6 @@ func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net
|
|||||||
errors.LogDebug(ctx, "CopyRawConn splice")
|
errors.LogDebug(ctx, "CopyRawConn splice")
|
||||||
statWriter, _ := writer.(*dispatcher.SizeStatWriter)
|
statWriter, _ := writer.(*dispatcher.SizeStatWriter)
|
||||||
//runtime.Gosched() // necessary
|
//runtime.Gosched() // necessary
|
||||||
time.Sleep(time.Millisecond) // without this, there will be a rare ssl error for freedom splice
|
|
||||||
timer.SetTimeout(24 * time.Hour) // prevent leak, just in case
|
timer.SetTimeout(24 * time.Hour) // prevent leak, just in case
|
||||||
if inTimer != nil {
|
if inTimer != nil {
|
||||||
inTimer.SetTimeout(24 * time.Hour)
|
inTimer.SetTimeout(24 * time.Hour)
|
||||||
|
|||||||
+24
-12
@@ -7,6 +7,7 @@
|
|||||||
package vless
|
package vless
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proxyman "github.com/xtls/xray-core/app/proxyman"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -22,8 +23,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Reverse struct {
|
type Reverse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
|
Sniffing *proxyman.SniffingConfig `protobuf:"bytes,2,opt,name=sniffing,proto3" json:"sniffing,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -65,6 +67,13 @@ func (x *Reverse) GetTag() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Reverse) GetSniffing() *proxyman.SniffingConfig {
|
||||||
|
if x != nil {
|
||||||
|
return x.Sniffing
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
|
// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
|
||||||
@@ -179,9 +188,10 @@ var File_proxy_vless_account_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_proxy_vless_account_proto_rawDesc = "" +
|
const file_proxy_vless_account_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x19proxy/vless/account.proto\x12\x10xray.proxy.vless\"\x1b\n" +
|
"\x19proxy/vless/account.proto\x12\x10xray.proxy.vless\x1a\x19app/proxyman/config.proto\"Z\n" +
|
||||||
"\aReverse\x12\x10\n" +
|
"\aReverse\x12\x10\n" +
|
||||||
"\x03tag\x18\x01 \x01(\tR\x03tag\"\x86\x02\n" +
|
"\x03tag\x18\x01 \x01(\tR\x03tag\x12=\n" +
|
||||||
|
"\bsniffing\x18\x02 \x01(\v2!.xray.app.proxyman.SniffingConfigR\bsniffing\"\x86\x02\n" +
|
||||||
"\aAccount\x12\x0e\n" +
|
"\aAccount\x12\x0e\n" +
|
||||||
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
|
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
|
||||||
"\x04flow\x18\x02 \x01(\tR\x04flow\x12\x1e\n" +
|
"\x04flow\x18\x02 \x01(\tR\x04flow\x12\x1e\n" +
|
||||||
@@ -210,16 +220,18 @@ func file_proxy_vless_account_proto_rawDescGZIP() []byte {
|
|||||||
|
|
||||||
var file_proxy_vless_account_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
var file_proxy_vless_account_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
var file_proxy_vless_account_proto_goTypes = []any{
|
var file_proxy_vless_account_proto_goTypes = []any{
|
||||||
(*Reverse)(nil), // 0: xray.proxy.vless.Reverse
|
(*Reverse)(nil), // 0: xray.proxy.vless.Reverse
|
||||||
(*Account)(nil), // 1: xray.proxy.vless.Account
|
(*Account)(nil), // 1: xray.proxy.vless.Account
|
||||||
|
(*proxyman.SniffingConfig)(nil), // 2: xray.app.proxyman.SniffingConfig
|
||||||
}
|
}
|
||||||
var file_proxy_vless_account_proto_depIdxs = []int32{
|
var file_proxy_vless_account_proto_depIdxs = []int32{
|
||||||
0, // 0: xray.proxy.vless.Account.reverse:type_name -> xray.proxy.vless.Reverse
|
2, // 0: xray.proxy.vless.Reverse.sniffing:type_name -> xray.app.proxyman.SniffingConfig
|
||||||
1, // [1:1] is the sub-list for method output_type
|
0, // 1: xray.proxy.vless.Account.reverse:type_name -> xray.proxy.vless.Reverse
|
||||||
1, // [1:1] is the sub-list for method input_type
|
2, // [2:2] is the sub-list for method output_type
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
2, // [2:2] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
0, // [0:1] is the sub-list for field type_name
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_proxy_vless_account_proto_init() }
|
func init() { file_proxy_vless_account_proto_init() }
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ option go_package = "github.com/xtls/xray-core/proxy/vless";
|
|||||||
option java_package = "com.xray.proxy.vless";
|
option java_package = "com.xray.proxy.vless";
|
||||||
option java_multiple_files = true;
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
import "app/proxyman/config.proto";
|
||||||
|
|
||||||
message Reverse {
|
message Reverse {
|
||||||
string tag = 1;
|
string tag = 1;
|
||||||
|
xray.app.proxyman.SniffingConfig sniffing = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Account {
|
message Account {
|
||||||
|
|||||||
@@ -97,14 +97,26 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if a.Reverse != nil {
|
if a.Reverse != nil {
|
||||||
|
rvsCtx := session.ContextWithInbound(ctx, &session.Inbound{
|
||||||
|
Tag: a.Reverse.Tag,
|
||||||
|
User: handler.server.User, // TODO: email
|
||||||
|
})
|
||||||
|
if sc := a.Reverse.Sniffing; sc != nil && sc.Enabled {
|
||||||
|
rvsCtx = session.ContextWithContent(rvsCtx, &session.Content{
|
||||||
|
SniffingRequest: session.SniffingRequest{
|
||||||
|
Enabled: sc.Enabled,
|
||||||
|
OverrideDestinationForProtocol: sc.DestinationOverride,
|
||||||
|
ExcludeForDomain: sc.DomainsExcluded,
|
||||||
|
MetadataOnly: sc.MetadataOnly,
|
||||||
|
RouteOnly: sc.RouteOnly,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
handler.reverse = &Reverse{
|
handler.reverse = &Reverse{
|
||||||
tag: a.Reverse.Tag,
|
tag: a.Reverse.Tag,
|
||||||
dispatcher: v.GetFeature(routing.DispatcherType()).(routing.Dispatcher),
|
dispatcher: v.GetFeature(routing.DispatcherType()).(routing.Dispatcher),
|
||||||
ctx: session.ContextWithInbound(ctx, &session.Inbound{
|
ctx: rvsCtx,
|
||||||
Tag: a.Reverse.Tag,
|
handler: handler,
|
||||||
User: handler.server.User, // TODO: email
|
|
||||||
}),
|
|
||||||
handler: handler,
|
|
||||||
}
|
}
|
||||||
handler.reverse.monitorTask = &task.Periodic{
|
handler.reverse.monitorTask = &task.Periodic{
|
||||||
Execute: handler.reverse.monitor,
|
Execute: handler.reverse.monitor,
|
||||||
|
|||||||
+45
-44
@@ -2,27 +2,23 @@ package wireguard
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
gonet "net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/conn"
|
"golang.zx2c4.com/wireguard/conn"
|
||||||
|
"golang.zx2c4.com/wireguard/device"
|
||||||
|
|
||||||
|
"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/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
type netReadInfo struct {
|
type netReadInfo struct {
|
||||||
// status
|
buff []byte
|
||||||
waiter sync.WaitGroup
|
|
||||||
// param
|
|
||||||
buff []byte
|
|
||||||
// result
|
|
||||||
bytes int
|
|
||||||
endpoint conn.Endpoint
|
endpoint conn.Endpoint
|
||||||
err error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reduce duplicated code
|
// reduce duplicated code
|
||||||
@@ -32,6 +28,7 @@ type netBind struct {
|
|||||||
|
|
||||||
workers int
|
workers int
|
||||||
readQueue chan *netReadInfo
|
readQueue chan *netReadInfo
|
||||||
|
closedCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMark implements conn.Bind
|
// SetMark implements conn.Bind
|
||||||
@@ -79,26 +76,23 @@ func (bind *netBind) BatchSize() int {
|
|||||||
|
|
||||||
// Open implements conn.Bind
|
// Open implements conn.Bind
|
||||||
func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
|
func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
|
||||||
bind.readQueue = make(chan *netReadInfo)
|
bind.closedCh = make(chan struct{})
|
||||||
|
errors.LogDebug(context.Background(), "bind opened")
|
||||||
|
|
||||||
fun := func(bufs [][]byte, sizes []int, eps []conn.Endpoint) (n int, err error) {
|
fun := func(bufs [][]byte, sizes []int, eps []conn.Endpoint) (n int, err error) {
|
||||||
defer func() {
|
select {
|
||||||
if r := recover(); r != nil {
|
case r := <-bind.readQueue:
|
||||||
n = 0
|
sizes[0], eps[0] = copy(bufs[0], r.buff), r.endpoint
|
||||||
err = errors.New("channel closed")
|
return 1, nil
|
||||||
}
|
case <-bind.closedCh:
|
||||||
}()
|
errors.LogDebug(context.Background(), "recv func closed")
|
||||||
|
return 0, gonet.ErrClosed
|
||||||
r := &netReadInfo{
|
|
||||||
buff: bufs[0],
|
|
||||||
}
|
}
|
||||||
r.waiter.Add(1)
|
|
||||||
bind.readQueue <- r
|
|
||||||
r.waiter.Wait() // wait read goroutine done, or we will miss the result
|
|
||||||
sizes[0], eps[0] = r.bytes, r.endpoint
|
|
||||||
return 1, r.err
|
|
||||||
}
|
}
|
||||||
workers := bind.workers
|
workers := bind.workers
|
||||||
|
if workers <= 0 {
|
||||||
|
workers = runtime.NumCPU()
|
||||||
|
}
|
||||||
if workers <= 0 {
|
if workers <= 0 {
|
||||||
workers = 1
|
workers = 1
|
||||||
}
|
}
|
||||||
@@ -112,8 +106,9 @@ func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
|
|||||||
|
|
||||||
// Close implements conn.Bind
|
// Close implements conn.Bind
|
||||||
func (bind *netBind) Close() error {
|
func (bind *netBind) Close() error {
|
||||||
if bind.readQueue != nil {
|
errors.LogDebug(context.Background(), "bind closed")
|
||||||
close(bind.readQueue)
|
if bind.closedCh != nil {
|
||||||
|
close(bind.closedCh)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -133,30 +128,35 @@ func (bind *netBindClient) connectTo(endpoint *netEndpoint) error {
|
|||||||
}
|
}
|
||||||
endpoint.conn = c
|
endpoint.conn = c
|
||||||
|
|
||||||
go func(readQueue <-chan *netReadInfo, endpoint *netEndpoint) {
|
go func() {
|
||||||
for {
|
for {
|
||||||
v, ok := <-readQueue
|
buff := make([]byte, device.MaxMessageSize)
|
||||||
if !ok {
|
n, err := c.Read(buff)
|
||||||
return
|
|
||||||
}
|
|
||||||
i, err := c.Read(v.buff)
|
|
||||||
|
|
||||||
if i > 3 {
|
|
||||||
v.buff[1] = 0
|
|
||||||
v.buff[2] = 0
|
|
||||||
v.buff[3] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
v.bytes = i
|
|
||||||
v.endpoint = endpoint
|
|
||||||
v.err = err
|
|
||||||
v.waiter.Done()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
endpoint.conn = nil
|
endpoint.conn = nil
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 3 {
|
||||||
|
buff[1] = 0
|
||||||
|
buff[2] = 0
|
||||||
|
buff[3] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case bind.readQueue <- &netReadInfo{
|
||||||
|
buff: buff[:n],
|
||||||
|
endpoint: endpoint,
|
||||||
|
}:
|
||||||
|
case <-bind.closedCh:
|
||||||
|
endpoint.conn = nil
|
||||||
|
c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}(bind.readQueue, endpoint)
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -200,7 +200,8 @@ func (bind *netBindServer) Send(buff [][]byte, endpoint conn.Endpoint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nend.conn == nil {
|
if nend.conn == nil {
|
||||||
return errors.New("connection not open yet")
|
errors.LogDebug(context.Background(), nend.dst.NetAddr(), " send on closed peer")
|
||||||
|
return errors.New("peer closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, buff := range buff {
|
for _, buff := range buff {
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
|
|||||||
IPv4Enable: h.hasIPv4,
|
IPv4Enable: h.hasIPv4,
|
||||||
IPv6Enable: h.hasIPv6,
|
IPv6Enable: h.hasIPv6,
|
||||||
},
|
},
|
||||||
workers: int(h.conf.NumWorkers),
|
workers: int(h.conf.NumWorkers),
|
||||||
|
readQueue: make(chan *netReadInfo),
|
||||||
},
|
},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
@@ -227,6 +228,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
conn = &udpConnClient{
|
||||||
|
Conn: conn,
|
||||||
|
dest: destination,
|
||||||
|
}
|
||||||
|
|
||||||
requestFunc = func() error {
|
requestFunc = func() error {
|
||||||
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
|
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
|
||||||
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
||||||
@@ -336,3 +342,34 @@ func (h *Handler) createIPCRequest() string {
|
|||||||
|
|
||||||
return request.String()[:request.Len()]
|
return request.String()[:request.Len()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type udpConnClient struct {
|
||||||
|
net.Conn
|
||||||
|
dest net.Destination
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConnClient) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
|
b := buf.New()
|
||||||
|
b.Resize(0, buf.Size)
|
||||||
|
n, addr, err := c.Conn.(net.PacketConn).ReadFrom(b.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
b.Release()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if addr == nil { // should never hit
|
||||||
|
addr = c.dest.RawNetAddr()
|
||||||
|
}
|
||||||
|
b.Resize(0, int32(n))
|
||||||
|
|
||||||
|
b.UDP = &net.Destination{
|
||||||
|
Address: net.IPAddress(addr.(*net.UDPAddr).IP),
|
||||||
|
Port: net.Port(addr.(*net.UDPAddr).Port),
|
||||||
|
Network: net.Network_UDP,
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.MultiBuffer{b}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConnClient) Write(p []byte) (int, error) {
|
||||||
|
return c.Conn.(net.PacketConn).WriteTo(p, c.dest.RawNetAddr())
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type netTun struct {
|
|||||||
ep *channel.Endpoint
|
ep *channel.Endpoint
|
||||||
stack *stack.Stack
|
stack *stack.Stack
|
||||||
events chan tun.Event
|
events chan tun.Event
|
||||||
|
notifyHandle *channel.NotificationHandle
|
||||||
incomingPacket chan *buffer.View
|
incomingPacket chan *buffer.View
|
||||||
mtu int
|
mtu int
|
||||||
hasV4, hasV6 bool
|
hasV4, hasV6 bool
|
||||||
@@ -48,12 +49,17 @@ func CreateNetTUN(localAddresses []netip.Addr, mtu int, promiscuousMode bool) (t
|
|||||||
dev := &netTun{
|
dev := &netTun{
|
||||||
ep: channel.New(1024, uint32(mtu), ""),
|
ep: channel.New(1024, uint32(mtu), ""),
|
||||||
stack: stack.New(opts),
|
stack: stack.New(opts),
|
||||||
events: make(chan tun.Event, 1),
|
events: make(chan tun.Event, 10),
|
||||||
incomingPacket: make(chan *buffer.View),
|
incomingPacket: make(chan *buffer.View),
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
}
|
}
|
||||||
dev.ep.AddNotify(dev)
|
sackEnabledOpt := tcpip.TCPSACKEnabled(true) // TCP SACK is disabled by default
|
||||||
tcpipErr := dev.stack.CreateNIC(1, dev.ep)
|
tcpipErr := dev.stack.SetTransportProtocolOption(tcp.ProtocolNumber, &sackEnabledOpt)
|
||||||
|
if tcpipErr != nil {
|
||||||
|
return nil, nil, dev.stack, fmt.Errorf("could not enable TCP SACK: %v", tcpipErr)
|
||||||
|
}
|
||||||
|
dev.notifyHandle = dev.ep.AddNotify(dev)
|
||||||
|
tcpipErr = dev.stack.CreateNIC(1, dev.ep)
|
||||||
if tcpipErr != nil {
|
if tcpipErr != nil {
|
||||||
return nil, nil, dev.stack, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
return nil, nil, dev.stack, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
||||||
}
|
}
|
||||||
@@ -90,20 +96,10 @@ func CreateNetTUN(localAddresses []netip.Addr, mtu int, promiscuousMode bool) (t
|
|||||||
dev.stack.SetSpoofing(1, true)
|
dev.stack.SetSpoofing(1, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
opt := tcpip.CongestionControlOption("cubic")
|
|
||||||
if err := dev.stack.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
|
||||||
return nil, nil, dev.stack, fmt.Errorf("SetTransportProtocolOption(%d, &%T(%s)): %s", tcp.ProtocolNumber, opt, opt, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dev.events <- tun.EventUp
|
dev.events <- tun.EventUp
|
||||||
return dev, (*Net)(dev), dev.stack, nil
|
return dev, (*Net)(dev), dev.stack, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchSize implements tun.Device
|
|
||||||
func (tun *netTun) BatchSize() int {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name implements tun.Device
|
// Name implements tun.Device
|
||||||
func (tun *netTun) Name() (string, error) {
|
func (tun *netTun) Name() (string, error) {
|
||||||
return "go", nil
|
return "go", nil
|
||||||
@@ -120,7 +116,6 @@ func (tun *netTun) Events() <-chan tun.Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read implements tun.Device
|
// Read implements tun.Device
|
||||||
|
|
||||||
func (tun *netTun) Read(buf [][]byte, sizes []int, offset int) (int, error) {
|
func (tun *netTun) Read(buf [][]byte, sizes []int, offset int) (int, error) {
|
||||||
view, ok := <-tun.incomingPacket
|
view, ok := <-tun.incomingPacket
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -169,20 +164,16 @@ func (tun *netTun) WriteNotify() {
|
|||||||
tun.incomingPacket <- view
|
tun.incomingPacket <- view
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush implements tun.Device
|
|
||||||
func (tun *netTun) Flush() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close implements tun.Device
|
// Close implements tun.Device
|
||||||
func (tun *netTun) Close() error {
|
func (tun *netTun) Close() error {
|
||||||
tun.closeOnce.Do(func() {
|
tun.closeOnce.Do(func() {
|
||||||
tun.stack.RemoveNIC(1)
|
tun.stack.RemoveNIC(1)
|
||||||
|
tun.stack.Close()
|
||||||
|
tun.ep.RemoveNotify(tun.notifyHandle)
|
||||||
|
tun.ep.Close()
|
||||||
|
|
||||||
close(tun.events)
|
close(tun.events)
|
||||||
|
|
||||||
tun.ep.Close()
|
|
||||||
|
|
||||||
close(tun.incomingPacket)
|
close(tun.incomingPacket)
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
@@ -193,6 +184,11 @@ func (tun *netTun) MTU() (int, error) {
|
|||||||
return tun.mtu, nil
|
return tun.mtu, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchSize implements tun.Device
|
||||||
|
func (tun *netTun) BatchSize() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||||
var protoNumber tcpip.NetworkProtocolNumber
|
var protoNumber tcpip.NetworkProtocolNumber
|
||||||
if endpoint.Addr().Is4() {
|
if endpoint.Addr().Is4() {
|
||||||
@@ -224,6 +220,7 @@ func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, er
|
|||||||
var addr tcpip.FullAddress
|
var addr tcpip.FullAddress
|
||||||
addr, pn = convertToFullAddr(raddr)
|
addr, pn = convertToFullAddr(raddr)
|
||||||
rfa = &addr
|
rfa = &addr
|
||||||
|
rfa = nil // do not ep connect
|
||||||
}
|
}
|
||||||
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-55
@@ -2,22 +2,18 @@ package wireguard
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
goerrors "errors"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
c "github.com/xtls/xray-core/common/ctx"
|
c "github.com/xtls/xray-core/common/ctx"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/log"
|
"github.com/xtls/xray-core/common/log"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/common/signal"
|
|
||||||
"github.com/xtls/xray-core/common/task"
|
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/policy"
|
"github.com/xtls/xray-core/features/policy"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,10 +27,10 @@ type Server struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type routingInfo struct {
|
type routingInfo struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
dispatcher routing.Dispatcher
|
dispatcher routing.Dispatcher
|
||||||
inboundTag *session.Inbound
|
inboundTag *session.Inbound
|
||||||
contentTag *session.Content
|
contentTag *session.Content
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) {
|
func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) {
|
||||||
@@ -53,6 +49,8 @@ func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) {
|
|||||||
IPv4Enable: hasIPv4,
|
IPv4Enable: hasIPv4,
|
||||||
IPv6Enable: hasIPv6,
|
IPv6Enable: hasIPv6,
|
||||||
},
|
},
|
||||||
|
workers: int(conf.NumWorkers),
|
||||||
|
readQueue: make(chan *netReadInfo),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||||
@@ -95,25 +93,31 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
|
|||||||
|
|
||||||
reader := buf.NewPacketReader(conn)
|
reader := buf.NewPacketReader(conn)
|
||||||
for {
|
for {
|
||||||
mpayload, err := reader.ReadMultiBuffer()
|
mb, err := reader.ReadMultiBuffer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
nep.conn = nil
|
||||||
|
buf.ReleaseMulti(mb)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, payload := range mpayload {
|
for i, b := range mb {
|
||||||
v, ok := <-s.bindServer.readQueue
|
buff := b.Bytes()
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
i, err := payload.Read(v.buff)
|
|
||||||
|
|
||||||
v.bytes = i
|
if b.Len() > 3 {
|
||||||
v.endpoint = nep
|
buff[1] = 0
|
||||||
v.err = err
|
buff[2] = 0
|
||||||
v.waiter.Done()
|
buff[3] = 0
|
||||||
if err != nil && goerrors.Is(err, io.EOF) {
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case s.bindServer.readQueue <- &netReadInfo{
|
||||||
|
buff: buff,
|
||||||
|
endpoint: nep,
|
||||||
|
}:
|
||||||
|
case <-s.bindServer.closedCh:
|
||||||
nep.conn = nil
|
nep.conn = nil
|
||||||
return nil
|
buf.ReleaseMulti(mb[i:])
|
||||||
|
return errors.New("bind closed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +128,6 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) {
|
|||||||
errors.LogError(s.info.ctx, "unexpected: dispatcher == nil")
|
errors.LogError(s.info.ctx, "unexpected: dispatcher == nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(core.ToBackgroundDetachedContext(s.info.ctx))
|
ctx, cancel := context.WithCancel(core.ToBackgroundDetachedContext(s.info.ctx))
|
||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
@@ -141,14 +144,13 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) {
|
|||||||
// Currently we have no way to link to the original source address
|
// Currently we have no way to link to the original source address
|
||||||
inbound.Source = net.DestinationFromAddr(conn.RemoteAddr())
|
inbound.Source = net.DestinationFromAddr(conn.RemoteAddr())
|
||||||
ctx = session.ContextWithInbound(ctx, &inbound)
|
ctx = session.ContextWithInbound(ctx, &inbound)
|
||||||
|
content := new(session.Content)
|
||||||
if s.info.contentTag != nil {
|
if s.info.contentTag != nil {
|
||||||
ctx = session.ContextWithContent(ctx, s.info.contentTag)
|
content.SniffingRequest = s.info.contentTag.SniffingRequest
|
||||||
}
|
}
|
||||||
|
ctx = session.ContextWithContent(ctx, content)
|
||||||
ctx = session.SubContextFromMuxInbound(ctx)
|
ctx = session.SubContextFromMuxInbound(ctx)
|
||||||
|
|
||||||
plcy := s.policyManager.ForLevel(0)
|
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle)
|
|
||||||
|
|
||||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||||
From: nullDestination,
|
From: nullDestination,
|
||||||
To: dest,
|
To: dest,
|
||||||
@@ -156,35 +158,15 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) {
|
|||||||
Reason: "",
|
Reason: "",
|
||||||
})
|
})
|
||||||
|
|
||||||
link, err := s.info.dispatcher.Dispatch(ctx, dest)
|
err := s.info.dispatcher.DispatchLink(ctx, dest, &transport.Link{
|
||||||
|
Reader: buf.NewReader(conn),
|
||||||
|
Writer: buf.NewWriter(conn),
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogErrorInner(ctx, err, "dispatch connection")
|
errors.LogInfoInner(ctx, err, "connection ends")
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
requestDone := func() error {
|
|
||||||
defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
|
|
||||||
if err := buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)); err != nil {
|
|
||||||
return errors.New("failed to transport all TCP request").Base(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
responseDone := func() error {
|
cancel()
|
||||||
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
conn.Close()
|
||||||
if err := buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)); err != nil {
|
|
||||||
return errors.New("failed to transport all TCP response").Base(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer))
|
|
||||||
if err := task.Run(ctx, requestDonePost, responseDone); err != nil {
|
|
||||||
common.Interrupt(link.Reader)
|
|
||||||
common.Interrupt(link.Writer)
|
|
||||||
errors.LogDebugInner(ctx, err, "connection ends")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+218
-36
@@ -3,6 +3,7 @@ package wireguard
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -10,12 +11,17 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"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/log"
|
"github.com/xtls/xray-core/common/log"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/proxy/wireguard/gvisortun"
|
"github.com/xtls/xray-core/proxy/wireguard/gvisortun"
|
||||||
|
"gvisor.dev/gvisor/pkg/buffer"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||||
"gvisor.dev/gvisor/pkg/waiter"
|
"gvisor.dev/gvisor/pkg/waiter"
|
||||||
@@ -138,7 +144,7 @@ func (g *gvisorNet) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, erro
|
|||||||
|
|
||||||
func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error) {
|
func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error) {
|
||||||
out := &gvisorNet{}
|
out := &gvisorNet{}
|
||||||
tun, n, stack, err := gvisortun.CreateNetTUN(localAddresses, mtu, handler != nil)
|
tun, n, gstack, err := gvisortun.CreateNetTUN(localAddresses, mtu, handler != nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -147,60 +153,236 @@ func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
|
|||||||
// handler is only used for promiscuous mode
|
// handler is only used for promiscuous mode
|
||||||
// capture all packets and send to handler
|
// capture all packets and send to handler
|
||||||
|
|
||||||
tcpForwarder := tcp.NewForwarder(stack, 0, 65535, func(r *tcp.ForwarderRequest) {
|
tcpForwarder := tcp.NewForwarder(gstack, 0, 65535, func(r *tcp.ForwarderRequest) {
|
||||||
go func(r *tcp.ForwarderRequest) {
|
go func(r *tcp.ForwarderRequest) {
|
||||||
var (
|
var wq waiter.Queue
|
||||||
wq waiter.Queue
|
var id = r.ID()
|
||||||
id = r.ID()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Perform a TCP three-way handshake.
|
|
||||||
ep, err := r.CreateEndpoint(&wq)
|
ep, err := r.CreateEndpoint(&wq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogError(context.Background(), err.String())
|
errors.LogError(context.Background(), err.String())
|
||||||
r.Complete(true)
|
r.Complete(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Complete(false)
|
|
||||||
defer ep.Close()
|
|
||||||
|
|
||||||
// enable tcp keep-alive to prevent hanging connections
|
options := ep.SocketOptions()
|
||||||
ep.SocketOptions().SetKeepAlive(true)
|
options.SetKeepAlive(false)
|
||||||
|
options.SetReuseAddress(true)
|
||||||
|
options.SetReusePort(true)
|
||||||
|
|
||||||
// local address is actually destination
|
|
||||||
handler(net.TCPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)), gonet.NewTCPConn(&wq, ep))
|
handler(net.TCPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)), gonet.NewTCPConn(&wq, ep))
|
||||||
|
|
||||||
|
ep.Close()
|
||||||
|
r.Complete(false)
|
||||||
}(r)
|
}(r)
|
||||||
})
|
})
|
||||||
stack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
gstack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||||
|
|
||||||
udpForwarder := udp.NewForwarder(stack, func(r *udp.ForwarderRequest) bool {
|
manager := &udpManager{
|
||||||
go func(r *udp.ForwarderRequest) {
|
stack: gstack,
|
||||||
var (
|
handler: handler,
|
||||||
wq waiter.Queue
|
m: make(map[string]*udpConn),
|
||||||
id = r.ID()
|
}
|
||||||
)
|
|
||||||
|
|
||||||
ep, err := r.CreateEndpoint(&wq)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogError(context.Background(), err.String())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer ep.Close()
|
|
||||||
|
|
||||||
// prevents hanging connections and ensure timely release
|
|
||||||
ep.SocketOptions().SetLinger(tcpip.LingerOption{
|
|
||||||
Enabled: true,
|
|
||||||
Timeout: 15 * time.Second,
|
|
||||||
})
|
|
||||||
|
|
||||||
handler(net.UDPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)), gonet.NewUDPConn(&wq, ep))
|
|
||||||
}(r)
|
|
||||||
|
|
||||||
|
gstack.SetTransportProtocolHandler(udp.ProtocolNumber, func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||||
|
data := pkt.Clone().Data().AsRange().ToSlice()
|
||||||
|
// if len(data) == 0 {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
src := net.UDPDestination(net.IPAddress(id.RemoteAddress.AsSlice()), net.Port(id.RemotePort))
|
||||||
|
dst := net.UDPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort))
|
||||||
|
manager.feed(src, dst, data)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.tun, out.net = tun, n
|
out.tun, out.net = tun, n
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type udpManager struct {
|
||||||
|
stack *stack.Stack
|
||||||
|
handler func(dest net.Destination, conn net.Conn)
|
||||||
|
m map[string]*udpConn
|
||||||
|
mutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *udpManager) feed(src net.Destination, dst net.Destination, data []byte) {
|
||||||
|
m.mutex.RLock()
|
||||||
|
uc, ok := m.m[src.NetAddr()]
|
||||||
|
if ok {
|
||||||
|
select {
|
||||||
|
case uc.ch <- data:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
m.mutex.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.mutex.RUnlock()
|
||||||
|
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
|
uc, ok = m.m[src.NetAddr()]
|
||||||
|
if !ok {
|
||||||
|
uc = &udpConn{
|
||||||
|
ch: make(chan []byte, 1024),
|
||||||
|
src: src,
|
||||||
|
dst: dst,
|
||||||
|
}
|
||||||
|
uc.writeFunc = m.writeRawUDPPacket
|
||||||
|
uc.closeFunc = func() {
|
||||||
|
m.mutex.Lock()
|
||||||
|
m.close(uc)
|
||||||
|
m.mutex.Unlock()
|
||||||
|
}
|
||||||
|
m.m[src.NetAddr()] = uc
|
||||||
|
go m.handler(dst, uc)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case uc.ch <- data:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *udpManager) close(uc *udpConn) {
|
||||||
|
if !uc.closed {
|
||||||
|
uc.closed = true
|
||||||
|
close(uc.ch)
|
||||||
|
delete(m.m, uc.src.NetAddr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *udpManager) writeRawUDPPacket(payload []byte, src net.Destination, dst net.Destination) error {
|
||||||
|
udpLen := header.UDPMinimumSize + len(payload)
|
||||||
|
srcIP := tcpip.AddrFromSlice(src.Address.IP())
|
||||||
|
dstIP := tcpip.AddrFromSlice(dst.Address.IP())
|
||||||
|
|
||||||
|
// build packet with appropriate IP header size
|
||||||
|
isIPv4 := dst.Address.Family().IsIPv4()
|
||||||
|
ipHdrSize := header.IPv6MinimumSize
|
||||||
|
ipProtocol := header.IPv6ProtocolNumber
|
||||||
|
if isIPv4 {
|
||||||
|
ipHdrSize = header.IPv4MinimumSize
|
||||||
|
ipProtocol = header.IPv4ProtocolNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
|
ReserveHeaderBytes: ipHdrSize + header.UDPMinimumSize,
|
||||||
|
Payload: buffer.MakeWithData(payload),
|
||||||
|
})
|
||||||
|
defer pkt.DecRef()
|
||||||
|
|
||||||
|
// Build UDP header
|
||||||
|
udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
|
||||||
|
udpHdr.Encode(&header.UDPFields{
|
||||||
|
SrcPort: uint16(src.Port),
|
||||||
|
DstPort: uint16(dst.Port),
|
||||||
|
Length: uint16(udpLen),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Calculate and set UDP checksum
|
||||||
|
xsum := header.PseudoHeaderChecksum(header.UDPProtocolNumber, srcIP, dstIP, uint16(udpLen))
|
||||||
|
udpHdr.SetChecksum(^udpHdr.CalculateChecksum(checksum.Checksum(payload, xsum)))
|
||||||
|
|
||||||
|
// Build IP header
|
||||||
|
if isIPv4 {
|
||||||
|
ipHdr := header.IPv4(pkt.NetworkHeader().Push(header.IPv4MinimumSize))
|
||||||
|
ipHdr.Encode(&header.IPv4Fields{
|
||||||
|
TotalLength: uint16(header.IPv4MinimumSize + udpLen),
|
||||||
|
TTL: 64,
|
||||||
|
Protocol: uint8(header.UDPProtocolNumber),
|
||||||
|
SrcAddr: srcIP,
|
||||||
|
DstAddr: dstIP,
|
||||||
|
})
|
||||||
|
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||||
|
} else {
|
||||||
|
ipHdr := header.IPv6(pkt.NetworkHeader().Push(header.IPv6MinimumSize))
|
||||||
|
ipHdr.Encode(&header.IPv6Fields{
|
||||||
|
PayloadLength: uint16(udpLen),
|
||||||
|
TransportProtocol: header.UDPProtocolNumber,
|
||||||
|
HopLimit: 64,
|
||||||
|
SrcAddr: srcIP,
|
||||||
|
DstAddr: dstIP,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatch the packet
|
||||||
|
err := m.stack.WriteRawPacket(1, ipProtocol, buffer.MakeWithView(pkt.ToView()))
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to write raw udp packet back to stack err ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type udpConn struct {
|
||||||
|
ch chan []byte
|
||||||
|
src net.Destination
|
||||||
|
dst net.Destination
|
||||||
|
writeFunc func(payload []byte, src net.Destination, dst net.Destination) error
|
||||||
|
closeFunc func()
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) Read(p []byte) (int, error) {
|
||||||
|
b, ok := <-c.ch
|
||||||
|
if !ok {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n := copy(p, b)
|
||||||
|
if n != len(b) {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
for i, b := range mb {
|
||||||
|
dst := c.dst
|
||||||
|
if b.UDP != nil {
|
||||||
|
dst = *b.UDP
|
||||||
|
}
|
||||||
|
err := c.writeFunc(b.Bytes(), dst, c.src)
|
||||||
|
if err != nil {
|
||||||
|
buf.ReleaseMulti(mb[i:])
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.Release()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) Write(p []byte) (int, error) {
|
||||||
|
err := c.writeFunc(p, c.dst, c.src)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) Close() error {
|
||||||
|
c.closeFunc()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) LocalAddr() net.Addr {
|
||||||
|
return c.src.RawNetAddr() // fake
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) RemoteAddr() net.Addr {
|
||||||
|
return c.src.RawNetAddr() // src
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) SetDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,25 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/log"
|
|
||||||
"golang.zx2c4.com/wireguard/device"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var wgLogger = &device.Logger{
|
|
||||||
Verbosef: func(format string, args ...any) {
|
|
||||||
log.Record(&log.GeneralMessage{
|
|
||||||
Severity: log.Severity_Debug,
|
|
||||||
Content: fmt.Sprintf(format, args...),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
Errorf: func(format string, args ...any) {
|
|
||||||
log.Record(&log.GeneralMessage{
|
|
||||||
Severity: log.Severity_Error,
|
|
||||||
Content: fmt.Sprintf(format, args...),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
deviceConfig := config.(*DeviceConfig)
|
deviceConfig := config.(*DeviceConfig)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/testing/servers/tcp"
|
"github.com/xtls/xray-core/testing/servers/tcp"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/reality"
|
"github.com/xtls/xray-core/transport/internet/reality"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
transtcp "github.com/xtls/xray-core/transport/internet/tcp"
|
transtcp "github.com/xtls/xray-core/transport/internet/tcp"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -647,3 +648,121 @@ func TestVlessRealityFingerprints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVlessXHTTPH2C(t *testing.T) {
|
||||||
|
tcpServer := tcp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err := tcpServer.Start()
|
||||||
|
common.Must(err)
|
||||||
|
defer tcpServer.Close()
|
||||||
|
|
||||||
|
userID := protocol.NewID(uuid.New())
|
||||||
|
serverPort := tcp.PickPort()
|
||||||
|
|
||||||
|
xhttpSettings := serial.ToTypedMessage(&splithttp.Config{
|
||||||
|
Path: "/h2c",
|
||||||
|
AllowH2C: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
serverConfig := &core.Config{
|
||||||
|
App: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
ErrorLogLevel: clog.Severity_Debug,
|
||||||
|
ErrorLogType: log.LogType_Console,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
Settings: xhttpSettings,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
|
Clients: []*protocol.User{
|
||||||
|
{
|
||||||
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
|
Id: userID.String(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPort := tcp.PickPort()
|
||||||
|
clientConfig := &core.Config{
|
||||||
|
App: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
ErrorLogLevel: clog.Severity_Debug,
|
||||||
|
ErrorLogType: log.LogType_Console,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
|
Address: net.NewIPOrDomain(dest.Address),
|
||||||
|
Port: uint32(dest.Port),
|
||||||
|
Networks: []net.Network{net.Network_TCP},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&outbound.Config{
|
||||||
|
Vnext: &protocol.ServerEndpoint{
|
||||||
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
Port: uint32(serverPort),
|
||||||
|
User: &protocol.User{
|
||||||
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
|
Id: userID.String(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
Settings: xhttpSettings,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||||
|
common.Must(err)
|
||||||
|
defer CloseAllServers(servers)
|
||||||
|
|
||||||
|
var errg errgroup.Group
|
||||||
|
for range 3 {
|
||||||
|
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
|
||||||
|
}
|
||||||
|
if err := errg.Wait(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ type Item struct {
|
|||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
RandMin int64 `protobuf:"varint,1,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
|
RandMin int64 `protobuf:"varint,1,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
|
||||||
RandMax int64 `protobuf:"varint,2,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
|
RandMax int64 `protobuf:"varint,2,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
|
||||||
Packet []byte `protobuf:"bytes,3,opt,name=packet,proto3" json:"packet,omitempty"`
|
RandRangeMin int32 `protobuf:"varint,3,opt,name=rand_range_min,json=randRangeMin,proto3" json:"rand_range_min,omitempty"`
|
||||||
DelayMin int64 `protobuf:"varint,4,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
RandRangeMax int32 `protobuf:"varint,4,opt,name=rand_range_max,json=randRangeMax,proto3" json:"rand_range_max,omitempty"`
|
||||||
DelayMax int64 `protobuf:"varint,5,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
Packet []byte `protobuf:"bytes,5,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||||
|
DelayMin int64 `protobuf:"varint,6,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||||
|
DelayMax int64 `protobuf:"varint,7,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -76,6 +78,20 @@ func (x *Item) GetRandMax() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Item) GetRandRangeMin() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.RandRangeMin
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Item) GetRandRangeMax() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.RandRangeMax
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Item) GetPacket() []byte {
|
func (x *Item) GetPacket() []byte {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Packet
|
return x.Packet
|
||||||
@@ -161,13 +177,15 @@ var File_transport_internet_finalmask_noise_config_proto protoreflect.FileDescri
|
|||||||
|
|
||||||
const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" +
|
const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"/transport/internet/finalmask/noise/config.proto\x12'xray.transport.internet.finalmask.noise\"\x8e\x01\n" +
|
"/transport/internet/finalmask/noise/config.proto\x12'xray.transport.internet.finalmask.noise\"\xda\x01\n" +
|
||||||
"\x04Item\x12\x19\n" +
|
"\x04Item\x12\x19\n" +
|
||||||
"\brand_min\x18\x01 \x01(\x03R\arandMin\x12\x19\n" +
|
"\brand_min\x18\x01 \x01(\x03R\arandMin\x12\x19\n" +
|
||||||
"\brand_max\x18\x02 \x01(\x03R\arandMax\x12\x16\n" +
|
"\brand_max\x18\x02 \x01(\x03R\arandMax\x12$\n" +
|
||||||
"\x06packet\x18\x03 \x01(\fR\x06packet\x12\x1b\n" +
|
"\x0erand_range_min\x18\x03 \x01(\x05R\frandRangeMin\x12$\n" +
|
||||||
"\tdelay_min\x18\x04 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
"\x0erand_range_max\x18\x04 \x01(\x05R\frandRangeMax\x12\x16\n" +
|
||||||
"\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\x87\x01\n" +
|
"\x06packet\x18\x05 \x01(\fR\x06packet\x12\x1b\n" +
|
||||||
|
"\tdelay_min\x18\x06 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
||||||
|
"\tdelay_max\x18\a \x01(\x03R\bdelayMax\"\x87\x01\n" +
|
||||||
"\x06Config\x12\x1b\n" +
|
"\x06Config\x12\x1b\n" +
|
||||||
"\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" +
|
"\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" +
|
||||||
"\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" +
|
"\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" +
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ option java_multiple_files = true;
|
|||||||
message Item {
|
message Item {
|
||||||
int64 rand_min = 1;
|
int64 rand_min = 1;
|
||||||
int64 rand_max = 2;
|
int64 rand_max = 2;
|
||||||
bytes packet = 3;
|
int32 rand_range_min = 3;
|
||||||
int64 delay_min = 4;
|
int32 rand_range_max = 4;
|
||||||
int64 delay_max = 5;
|
bytes packet = 5;
|
||||||
|
int64 delay_min = 6;
|
||||||
|
int64 delay_max = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package noise
|
package noise
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/crypto"
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,7 +75,7 @@ func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||||||
for _, item := range c.config.Items {
|
for _, item := range c.config.Items {
|
||||||
if item.RandMax > 0 {
|
if item.RandMax > 0 {
|
||||||
item.Packet = make([]byte, crypto.RandBetween(item.RandMin, item.RandMax))
|
item.Packet = make([]byte, crypto.RandBetween(item.RandMin, item.RandMax))
|
||||||
common.Must2(rand.Read(item.Packet))
|
crypto.RandBytesBetween(item.Packet, byte(item.RandRangeMin), byte(item.RandRangeMax))
|
||||||
}
|
}
|
||||||
c.PacketConn.WriteTo(item.Packet, addr)
|
c.PacketConn.WriteTo(item.Packet, addr)
|
||||||
time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond)
|
time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond)
|
||||||
|
|||||||
@@ -100,32 +100,39 @@ func (m *udpSessionManagerServer) run() {
|
|||||||
func (m *udpSessionManagerServer) feed(id uint32, d []byte) {
|
func (m *udpSessionManagerServer) feed(id uint32, d []byte) {
|
||||||
m.mutex.RLock()
|
m.mutex.RLock()
|
||||||
udpConn, ok := m.m[id]
|
udpConn, ok := m.m[id]
|
||||||
|
if ok {
|
||||||
|
select {
|
||||||
|
case udpConn.ch <- d:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
m.mutex.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
m.mutex.RUnlock()
|
m.mutex.RUnlock()
|
||||||
|
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
|
udpConn, ok = m.m[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
m.mutex.Lock()
|
udpConn = &InterUdpConn{
|
||||||
udpConn, ok = m.m[id]
|
conn: m.conn,
|
||||||
if !ok {
|
local: m.conn.LocalAddr(),
|
||||||
udpConn = &InterUdpConn{
|
remote: m.conn.RemoteAddr(),
|
||||||
conn: m.conn,
|
|
||||||
local: m.conn.LocalAddr(),
|
|
||||||
remote: m.conn.RemoteAddr(),
|
|
||||||
|
|
||||||
id: id,
|
id: id,
|
||||||
ch: make(chan []byte, udpMessageChanSize),
|
ch: make(chan []byte, udpMessageChanSize),
|
||||||
last: time.Now(),
|
last: time.Now(),
|
||||||
|
|
||||||
user: m.user,
|
user: m.user,
|
||||||
}
|
|
||||||
udpConn.closeFunc = func() {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
m.close(udpConn)
|
|
||||||
}
|
|
||||||
m.m[id] = udpConn
|
|
||||||
m.addConn(udpConn)
|
|
||||||
}
|
}
|
||||||
m.mutex.Unlock()
|
udpConn.closeFunc = func() {
|
||||||
|
m.mutex.Lock()
|
||||||
|
m.close(udpConn)
|
||||||
|
m.mutex.Unlock()
|
||||||
|
}
|
||||||
|
m.m[id] = udpConn
|
||||||
|
m.addConn(udpConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ type Config struct {
|
|||||||
UplinkDataKey string `protobuf:"bytes,25,opt,name=uplinkDataKey,proto3" json:"uplinkDataKey,omitempty"`
|
UplinkDataKey string `protobuf:"bytes,25,opt,name=uplinkDataKey,proto3" json:"uplinkDataKey,omitempty"`
|
||||||
UplinkChunkSize *RangeConfig `protobuf:"bytes,26,opt,name=uplinkChunkSize,proto3" json:"uplinkChunkSize,omitempty"`
|
UplinkChunkSize *RangeConfig `protobuf:"bytes,26,opt,name=uplinkChunkSize,proto3" json:"uplinkChunkSize,omitempty"`
|
||||||
ServerMaxHeaderBytes int32 `protobuf:"varint,27,opt,name=serverMaxHeaderBytes,proto3" json:"serverMaxHeaderBytes,omitempty"`
|
ServerMaxHeaderBytes int32 `protobuf:"varint,27,opt,name=serverMaxHeaderBytes,proto3" json:"serverMaxHeaderBytes,omitempty"`
|
||||||
|
AllowH2C bool `protobuf:"varint,28,opt,name=allow_h2c,json=allowH2c,proto3" json:"allow_h2c,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -410,6 +411,13 @@ func (x *Config) GetServerMaxHeaderBytes() int32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetAllowH2C() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.AllowH2C
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var File_transport_internet_splithttp_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_splithttp_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_transport_internet_splithttp_config_proto_rawDesc = "" +
|
const file_transport_internet_splithttp_config_proto_rawDesc = "" +
|
||||||
@@ -425,7 +433,7 @@ const file_transport_internet_splithttp_config_proto_rawDesc = "" +
|
|||||||
"\x0ecMaxReuseTimes\x18\x03 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0ecMaxReuseTimes\x12Z\n" +
|
"\x0ecMaxReuseTimes\x18\x03 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0ecMaxReuseTimes\x12Z\n" +
|
||||||
"\x10hMaxRequestTimes\x18\x04 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxRequestTimes\x12Z\n" +
|
"\x10hMaxRequestTimes\x18\x04 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxRequestTimes\x12Z\n" +
|
||||||
"\x10hMaxReusableSecs\x18\x05 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxReusableSecs\x12*\n" +
|
"\x10hMaxReusableSecs\x18\x05 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxReusableSecs\x12*\n" +
|
||||||
"\x10hKeepAlivePeriod\x18\x06 \x01(\x03R\x10hKeepAlivePeriod\"\xc2\v\n" +
|
"\x10hKeepAlivePeriod\x18\x06 \x01(\x03R\x10hKeepAlivePeriod\"\xdf\v\n" +
|
||||||
"\x06Config\x12\x12\n" +
|
"\x06Config\x12\x12\n" +
|
||||||
"\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" +
|
"\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" +
|
||||||
"\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" +
|
"\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" +
|
||||||
@@ -456,7 +464,8 @@ const file_transport_internet_splithttp_config_proto_rawDesc = "" +
|
|||||||
"\x13uplinkDataPlacement\x18\x18 \x01(\tR\x13uplinkDataPlacement\x12$\n" +
|
"\x13uplinkDataPlacement\x18\x18 \x01(\tR\x13uplinkDataPlacement\x12$\n" +
|
||||||
"\ruplinkDataKey\x18\x19 \x01(\tR\ruplinkDataKey\x12X\n" +
|
"\ruplinkDataKey\x18\x19 \x01(\tR\ruplinkDataKey\x12X\n" +
|
||||||
"\x0fuplinkChunkSize\x18\x1a \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0fuplinkChunkSize\x122\n" +
|
"\x0fuplinkChunkSize\x18\x1a \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0fuplinkChunkSize\x122\n" +
|
||||||
"\x14serverMaxHeaderBytes\x18\x1b \x01(\x05R\x14serverMaxHeaderBytes\x1a:\n" +
|
"\x14serverMaxHeaderBytes\x18\x1b \x01(\x05R\x14serverMaxHeaderBytes\x12\x1b\n" +
|
||||||
|
"\tallow_h2c\x18\x1c \x01(\bR\ballowH2c\x1a:\n" +
|
||||||
"\fHeadersEntry\x12\x10\n" +
|
"\fHeadersEntry\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x85\x01\n" +
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x85\x01\n" +
|
||||||
|
|||||||
@@ -50,4 +50,5 @@ message Config {
|
|||||||
string uplinkDataKey = 25;
|
string uplinkDataKey = 25;
|
||||||
RangeConfig uplinkChunkSize = 26;
|
RangeConfig uplinkChunkSize = 26;
|
||||||
int32 serverMaxHeaderBytes = 27;
|
int32 serverMaxHeaderBytes = 27;
|
||||||
|
bool allow_h2c = 28;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,11 +79,14 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
return xmuxClient.XmuxConn.(DialerClient), xmuxClient
|
return xmuxClient.XmuxConn.(DialerClient), xmuxClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func decideHTTPVersion(tlsConfig *tls.Config, realityConfig *reality.Config) string {
|
func decideHTTPVersion(tlsConfig *tls.Config, realityConfig *reality.Config, allowH2C bool) string {
|
||||||
if realityConfig != nil {
|
if realityConfig != nil {
|
||||||
return "2"
|
return "2"
|
||||||
}
|
}
|
||||||
if tlsConfig == nil {
|
if tlsConfig == nil {
|
||||||
|
if allowH2C {
|
||||||
|
return "2"
|
||||||
|
}
|
||||||
return "1.1"
|
return "1.1"
|
||||||
}
|
}
|
||||||
if len(tlsConfig.NextProtocol) != 1 {
|
if len(tlsConfig.NextProtocol) != 1 {
|
||||||
@@ -101,8 +104,9 @@ func decideHTTPVersion(tlsConfig *tls.Config, realityConfig *reality.Config) str
|
|||||||
func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient {
|
func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient {
|
||||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
||||||
|
transportConfig := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
|
||||||
httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
|
httpVersion := decideHTTPVersion(tlsConfig, realityConfig, transportConfig.AllowH2C)
|
||||||
if httpVersion == "3" {
|
if httpVersion == "3" {
|
||||||
dest.Network = net.Network_UDP // better to keep this line
|
dest.Network = net.Network_UDP // better to keep this line
|
||||||
}
|
}
|
||||||
@@ -113,8 +117,6 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
gotlsConfig = tlsConfig.GetTLSConfig(tls.WithDestination(dest))
|
gotlsConfig = tlsConfig.GetTLSConfig(tls.WithDestination(dest))
|
||||||
}
|
}
|
||||||
|
|
||||||
transportConfig := streamSettings.ProtocolSettings.(*Config)
|
|
||||||
|
|
||||||
dialContext := func(ctxInner context.Context) (net.Conn, error) {
|
dialContext := func(ctxInner context.Context) (net.Conn, error) {
|
||||||
conn, err := internet.DialSystem(ctxInner, dest, streamSettings.SocketSettings)
|
conn, err := internet.DialSystem(ctxInner, dest, streamSettings.SocketSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -312,6 +314,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
},
|
},
|
||||||
IdleConnTimeout: net.ConnIdleTimeout,
|
IdleConnTimeout: net.ConnIdleTimeout,
|
||||||
ReadIdleTimeout: keepAlivePeriod,
|
ReadIdleTimeout: keepAlivePeriod,
|
||||||
|
AllowHTTP: transportConfig.AllowH2C,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
httpDialContext := func(ctxInner context.Context, network string, addr string) (net.Conn, error) {
|
httpDialContext := func(ctxInner context.Context, network string, addr string) (net.Conn, error) {
|
||||||
@@ -348,13 +351,12 @@ func init() {
|
|||||||
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) {
|
||||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
||||||
|
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
|
||||||
httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
|
httpVersion := decideHTTPVersion(tlsConfig, realityConfig, transportConfiguration.AllowH2C)
|
||||||
if httpVersion == "3" {
|
if httpVersion == "3" {
|
||||||
dest.Network = net.Network_UDP
|
dest.Network = net.Network_UDP
|
||||||
}
|
}
|
||||||
|
|
||||||
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
|
|
||||||
var requestURL url.URL
|
var requestURL url.URL
|
||||||
|
|
||||||
if tlsConfig != nil || realityConfig != nil {
|
if tlsConfig != nil || realityConfig != nil {
|
||||||
@@ -413,7 +415,8 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
dest2 := *memory2.Destination // just panic
|
dest2 := *memory2.Destination // just panic
|
||||||
tlsConfig2 := tls.ConfigFromStreamSettings(memory2)
|
tlsConfig2 := tls.ConfigFromStreamSettings(memory2)
|
||||||
realityConfig2 := reality.ConfigFromStreamSettings(memory2)
|
realityConfig2 := reality.ConfigFromStreamSettings(memory2)
|
||||||
httpVersion2 := decideHTTPVersion(tlsConfig2, realityConfig2)
|
config2 := memory2.ProtocolSettings.(*Config)
|
||||||
|
httpVersion2 := decideHTTPVersion(tlsConfig2, realityConfig2, config2.AllowH2C)
|
||||||
if httpVersion2 == "3" {
|
if httpVersion2 == "3" {
|
||||||
dest2.Network = net.Network_UDP
|
dest2.Network = net.Network_UDP
|
||||||
}
|
}
|
||||||
@@ -422,7 +425,6 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
} else {
|
} else {
|
||||||
requestURL2.Scheme = "http"
|
requestURL2.Scheme = "http"
|
||||||
}
|
}
|
||||||
config2 := memory2.ProtocolSettings.(*Config)
|
|
||||||
requestURL2.Host = config2.Host
|
requestURL2.Host = config2.Host
|
||||||
if requestURL2.Host == "" && tlsConfig2 != nil {
|
if requestURL2.Host == "" && tlsConfig2 != nil {
|
||||||
requestURL2.Host = tlsConfig2.ServerName
|
requestURL2.Host = tlsConfig2.ServerName
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -186,39 +185,60 @@ func Test_ListenXHAndDial_H2C(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
listenPort := tcp.PickPort()
|
listenPort := tcp.PickPort()
|
||||||
|
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
||||||
streamSettings := &internet.MemoryStreamConfig{
|
|
||||||
ProtocolName: "splithttp",
|
ProtocolName: "splithttp",
|
||||||
ProtocolSettings: &Config{
|
ProtocolSettings: &Config{
|
||||||
Path: "shs",
|
Path: "/sh",
|
||||||
|
AllowH2C: true,
|
||||||
},
|
},
|
||||||
}
|
}, func(conn stat.Connection) {
|
||||||
listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
go func(c stat.Connection) {
|
||||||
go func() {
|
defer c.Close()
|
||||||
_ = conn.Close()
|
|
||||||
}()
|
var b [1024]byte
|
||||||
|
c.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
_, err := c.Read(b[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Must2(c.Write([]byte("Response")))
|
||||||
|
}(conn)
|
||||||
})
|
})
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
defer listen.Close()
|
ctx := context.Background()
|
||||||
|
streamSettings := &internet.MemoryStreamConfig{
|
||||||
protocols := new(http.Protocols)
|
ProtocolName: "splithttp",
|
||||||
protocols.SetUnencryptedHTTP2(true)
|
ProtocolSettings: &Config{Path: "sh", AllowH2C: true},
|
||||||
client := http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
Protocols: protocols,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
|
|
||||||
resp, err := client.Get("http://" + net.LocalHostIP.String() + ":" + listenPort.String())
|
common.Must(err)
|
||||||
|
_, err = conn.Write([]byte("Test connection 1"))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
if resp.StatusCode != 404 {
|
var b [1024]byte
|
||||||
t.Error("Expected 404 but got:", resp.StatusCode)
|
fmt.Println("test2")
|
||||||
|
n, _ := io.ReadFull(conn, b[:])
|
||||||
|
fmt.Println("string is", n)
|
||||||
|
if string(b[:n]) != "Response" {
|
||||||
|
t.Error("response: ", string(b[:n]))
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.ProtoMajor != 2 {
|
common.Must(conn.Close())
|
||||||
t.Error("Expected h2 but got:", resp.ProtoMajor)
|
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
|
|
||||||
|
common.Must(err)
|
||||||
|
_, err = conn.Write([]byte("Test connection 2"))
|
||||||
|
common.Must(err)
|
||||||
|
n, _ = io.ReadFull(conn, b[:])
|
||||||
|
common.Must(err)
|
||||||
|
if string(b[:n]) != "Response" {
|
||||||
|
t.Error("response: ", string(b[:n]))
|
||||||
}
|
}
|
||||||
|
common.Must(conn.Close())
|
||||||
|
|
||||||
|
common.Must(listen.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_ListenXHAndDial_QUIC(t *testing.T) {
|
func Test_ListenXHAndDial_QUIC(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user