mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-14 10:00:34 +00:00
XTLS Vision: Defer Splice handoff until write completes (#5737)
Fixes https://github.com/XTLS/Xray-core/issues/4878
This commit is contained in:
+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)
|
||||||
|
|||||||
Reference in New Issue
Block a user