mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-05-17 17:48:40 +00:00
Add seed padding logic
- Seed is decoupled with XTLS Vision, which means Seed can turn on without flow - XTLS Vision now use Seed config to configure its padding only mode
This commit is contained in:
parent
68881b531e
commit
5abdc38437
8 changed files with 632 additions and 51 deletions
|
@ -13,6 +13,7 @@ import (
|
|||
"math/big"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
|
@ -163,6 +164,7 @@ func NewTrafficState(userUUID []byte, flow string) *TrafficState {
|
|||
RemainingPadding: -1,
|
||||
CurrentCommand: 0,
|
||||
DownlinkWriterDirectCopy: false,
|
||||
IsPadding: true,
|
||||
},
|
||||
Outbound: OutboundState{
|
||||
DownlinkReaderDirectCopy: false,
|
||||
|
@ -171,11 +173,10 @@ func NewTrafficState(userUUID []byte, flow string) *TrafficState {
|
|||
RemainingPadding: -1,
|
||||
CurrentCommand: 0,
|
||||
UplinkWriterDirectCopy: false,
|
||||
IsPadding: true,
|
||||
},
|
||||
}
|
||||
if len(flow) > 0 {
|
||||
state.Inbound.IsPadding = true;
|
||||
state.Outbound.IsPadding = true;
|
||||
state.Inbound.WithinPaddingBuffers = true;
|
||||
state.Outbound.WithinPaddingBuffers = true;
|
||||
}
|
||||
|
@ -186,14 +187,16 @@ func NewTrafficState(userUUID []byte, flow string) *TrafficState {
|
|||
// Note Vision probably only make sense as the inner most layer of reader, since it need assess traffic state from origin proxy traffic
|
||||
type VisionReader struct {
|
||||
buf.Reader
|
||||
addons *Addons
|
||||
trafficState *TrafficState
|
||||
ctx context.Context
|
||||
isUplink bool
|
||||
}
|
||||
|
||||
func NewVisionReader(reader buf.Reader, state *TrafficState, isUplink bool, context context.Context) *VisionReader {
|
||||
func NewVisionReader(reader buf.Reader, addon *Addons, state *TrafficState, isUplink bool, context context.Context) *VisionReader {
|
||||
return &VisionReader{
|
||||
Reader: reader,
|
||||
addons: addon,
|
||||
trafficState: state,
|
||||
ctx: context,
|
||||
isUplink: isUplink,
|
||||
|
@ -226,7 +229,7 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||
switchToDirectCopy = &w.trafficState.Outbound.DownlinkReaderDirectCopy
|
||||
}
|
||||
|
||||
if *withinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 {
|
||||
if *withinPaddingBuffers || !ShouldStopSeed(w.addons, w.trafficState) {
|
||||
mb2 := make(buf.MultiBuffer, 0, len(buffer))
|
||||
for _, b := range buffer {
|
||||
newbuffer := XtlsUnpadding(b, w.trafficState, w.isUplink, w.ctx)
|
||||
|
@ -258,17 +261,19 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||
// Note Vision probably only make sense as the inner most layer of writer, since it need assess traffic state from origin proxy traffic
|
||||
type VisionWriter struct {
|
||||
buf.Writer
|
||||
addons *Addons
|
||||
trafficState *TrafficState
|
||||
ctx context.Context
|
||||
writeOnceUserUUID []byte
|
||||
isUplink bool
|
||||
}
|
||||
|
||||
func NewVisionWriter(writer buf.Writer, state *TrafficState, isUplink bool, context context.Context) *VisionWriter {
|
||||
func NewVisionWriter(writer buf.Writer, addon *Addons, state *TrafficState, isUplink bool, context context.Context) *VisionWriter {
|
||||
w := make([]byte, len(state.UserUUID))
|
||||
copy(w, state.UserUUID)
|
||||
return &VisionWriter{
|
||||
Writer: writer,
|
||||
addons: addon,
|
||||
trafficState: state,
|
||||
ctx: context,
|
||||
writeOnceUserUUID: w,
|
||||
|
@ -290,9 +295,9 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||
isPadding = &w.trafficState.Inbound.IsPadding
|
||||
switchToDirectCopy = &w.trafficState.Inbound.DownlinkWriterDirectCopy
|
||||
}
|
||||
if *isPadding {
|
||||
if *isPadding && ShouldStartSeed(w.addons, w.trafficState) {
|
||||
if len(mb) == 1 && mb[0] == nil {
|
||||
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
|
||||
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.addons, w.ctx) // we do a long padding to hide vless header
|
||||
} else {
|
||||
mb = ReshapeMultiBuffer(w.ctx, mb)
|
||||
longPadding := w.trafficState.IsTLS
|
||||
|
@ -303,18 +308,20 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||
}
|
||||
var command byte = CommandPaddingContinue
|
||||
if i == len(mb) - 1 {
|
||||
command = CommandPaddingEnd
|
||||
if w.trafficState.EnableXtls {
|
||||
command = CommandPaddingDirect
|
||||
*isPadding = false
|
||||
} else if ShouldStopSeed(w.addons, w.trafficState) {
|
||||
command = CommandPaddingEnd
|
||||
*isPadding = false
|
||||
}
|
||||
}
|
||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx)
|
||||
*isPadding = false // padding going to end
|
||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.addons, w.ctx)
|
||||
longPadding = false
|
||||
continue
|
||||
} else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
|
||||
} else if !w.trafficState.IsTLS12orAbove && ShouldStopSeed(w.addons, w.trafficState) {
|
||||
*isPadding = false
|
||||
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx)
|
||||
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.addons, w.ctx)
|
||||
break
|
||||
}
|
||||
var command byte = CommandPaddingContinue
|
||||
|
@ -324,7 +331,7 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||
command = CommandPaddingDirect
|
||||
}
|
||||
}
|
||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx)
|
||||
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.addons, w.ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -371,24 +378,24 @@ func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBu
|
|||
}
|
||||
|
||||
// XtlsPadding add padding to eliminate length signature during tls handshake
|
||||
func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer {
|
||||
func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, addons *Addons, ctx context.Context) *buf.Buffer {
|
||||
var contentLen int32 = 0
|
||||
var paddingLen int32 = 0
|
||||
if b != nil {
|
||||
contentLen = b.Len()
|
||||
}
|
||||
if contentLen < 900 && longPadding {
|
||||
l, err := rand.Int(rand.Reader, big.NewInt(500))
|
||||
if contentLen < int32(addons.Padding.LongMin) && longPadding {
|
||||
l, err := rand.Int(rand.Reader, big.NewInt(int64(addons.Padding.LongMax - addons.Padding.LongMin)))
|
||||
if err != nil {
|
||||
errors.LogDebugInner(ctx, err, "failed to generate padding")
|
||||
}
|
||||
paddingLen = int32(l.Int64()) + 900 - contentLen
|
||||
paddingLen = int32(l.Int64()) + int32(addons.Padding.LongMin) - contentLen
|
||||
} else {
|
||||
l, err := rand.Int(rand.Reader, big.NewInt(256))
|
||||
l, err := rand.Int(rand.Reader, big.NewInt(int64(addons.Padding.RegularMax - addons.Padding.RegularMin)))
|
||||
if err != nil {
|
||||
errors.LogDebugInner(ctx, err, "failed to generate padding")
|
||||
}
|
||||
paddingLen = int32(l.Int64())
|
||||
paddingLen = int32(l.Int64()) + int32(addons.Padding.RegularMin)
|
||||
}
|
||||
if paddingLen > buf.Size-21-contentLen {
|
||||
paddingLen = buf.Size - 21 - contentLen
|
||||
|
@ -661,3 +668,50 @@ func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer sign
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ShouldStartSeed(addons *Addons, trafficState *TrafficState) bool {
|
||||
if len(addons.Duration) == 0 || len(strings.Split(addons.Duration, "-")) < 2 {
|
||||
return false
|
||||
}
|
||||
start := strings.ToLower(strings.Split(addons.Duration, "-")[0])
|
||||
if len(start) == 0 {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(start, "b") {
|
||||
start = strings.TrimRight(start, "b")
|
||||
i, err := strconv.Atoi(start)
|
||||
if err == nil && i <= int(trafficState.ByteSent + trafficState.ByteSent) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
i, err := strconv.Atoi(start)
|
||||
if err == nil && i <= trafficState.NumberOfPacketSent + trafficState.NumberOfPacketReceived {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ShouldStopSeed(addons *Addons, trafficState *TrafficState) bool {
|
||||
if len(addons.Duration) == 0 || len(strings.Split(addons.Duration, "-")) < 2 {
|
||||
return true
|
||||
}
|
||||
start := strings.ToLower(strings.Split(addons.Duration, "-")[1])
|
||||
if len(start) == 0 { // infinite
|
||||
return false
|
||||
}
|
||||
if strings.Contains(start, "b") {
|
||||
start = strings.TrimRight(start, "b")
|
||||
i, err := strconv.Atoi(start)
|
||||
if err == nil && i > int(trafficState.ByteSent + trafficState.ByteSent) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
i, err := strconv.Atoi(start)
|
||||
if err == nil && i > trafficState.NumberOfPacketSent + trafficState.NumberOfPacketReceived {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue