Skip to content

Commit

Permalink
Experiment: pingpong seed
Browse files Browse the repository at this point in the history
  • Loading branch information
yuhan6665 committed Nov 29, 2024
1 parent 80ce4a2 commit 7743a2b
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 300 deletions.
29 changes: 14 additions & 15 deletions common/buf/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (

type dataHandler func(MultiBuffer)

type copyHandler struct {
onData []dataHandler
type CopyHandler struct {
OnData []dataHandler
}

// SizeCounter is for counting bytes copied by Copy().
Expand All @@ -21,30 +21,30 @@ type SizeCounter struct {
}

// CopyOption is an option for copying data.
type CopyOption func(*copyHandler)
type CopyOption func(*CopyHandler)

// UpdateActivity is a CopyOption to update activity on each data copy operation.
func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
return func(handler *copyHandler) {
handler.onData = append(handler.onData, func(MultiBuffer) {
return func(handler *CopyHandler) {
handler.OnData = append(handler.OnData, func(MultiBuffer) {
timer.Update()
})
}
}

// CountSize is a CopyOption that sums the total size of data copied into the given SizeCounter.
func CountSize(sc *SizeCounter) CopyOption {
return func(handler *copyHandler) {
handler.onData = append(handler.onData, func(b MultiBuffer) {
return func(handler *CopyHandler) {
handler.OnData = append(handler.OnData, func(b MultiBuffer) {
sc.Size += int64(b.Len())
})
}
}

// AddToStatCounter a CopyOption add to stat counter
func AddToStatCounter(sc stats.Counter) CopyOption {
return func(handler *copyHandler) {
handler.onData = append(handler.onData, func(b MultiBuffer) {
return func(handler *CopyHandler) {
handler.OnData = append(handler.OnData, func(b MultiBuffer) {
if sc != nil {
sc.Add(int64(b.Len()))
}
Expand Down Expand Up @@ -88,18 +88,17 @@ func IsWriteError(err error) bool {
return ok
}

func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
func copyInternal(reader Reader, writer Writer, handler *CopyHandler) error {
for {
buffer, err := reader.ReadMultiBuffer()
if !buffer.IsEmpty() {
for _, handler := range handler.onData {
handler(buffer)
}

if werr := writer.WriteMultiBuffer(buffer); werr != nil {
return writeError{werr}
}
}
for _, handler := range handler.OnData {
handler(buffer)
}

if err != nil {
return readError{err}
Expand All @@ -109,7 +108,7 @@ func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {

// Copy dumps all payload from reader to writer or stops when an error occurs. It returns nil when EOF.
func Copy(reader Reader, writer Writer, options ...CopyOption) error {
var handler copyHandler
var handler CopyHandler
for _, option := range options {
option(&handler)
}
Expand Down
49 changes: 29 additions & 20 deletions proxy/addons.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions proxy/addons.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ message DelayConfig {

message SchedulerConfig {
uint32 TimeoutMillis = 1; // original traffic will not be sent right away but when scheduler want to send or pending buffer times out
bool PingPong = 2;
// Other TBD
}
2 changes: 1 addition & 1 deletion proxy/freedom/freedom.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
inTimer = inbound.Timer
}
if !isTLSConn(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer, nil)
}
}
var reader buf.Reader
Expand Down
33 changes: 24 additions & 9 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ type VisionWriter struct {
trafficState *TrafficState
ctx context.Context
writeOnceUserUUID *[]byte
scheduler *Scheduler
Scheduler *Scheduler
}

func NewVisionWriter(writer buf.Writer, addon *Addons, state *TrafficState, context context.Context) *VisionWriter {
Expand All @@ -228,7 +228,7 @@ func NewVisionWriter(writer buf.Writer, addon *Addons, state *TrafficState, cont
trafficState: state,
ctx: context,
writeOnceUserUUID: &w,
scheduler: NewScheduler(writer, addon, state, &w, context),
Scheduler: NewScheduler(writer, addon, state, &w, context),
}
}

Expand Down Expand Up @@ -281,12 +281,24 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if w.trafficState.StartTime.IsZero() {
w.trafficState.StartTime = time.Now()
}
w.scheduler.Buffer <- mb
if w.addons.Scheduler == nil {
w.scheduler.Trigger <- -1 // send all buffers
w.Scheduler.Buffer <- mb
w.Scheduler.Trigger <- -1 // send all buffers if no independent scheduler
if w.addons.Scheduler != nil {
w.Scheduler.TimeoutLock.Lock()
w.Scheduler.TimeoutCounter++
w.Scheduler.TimeoutLock.Unlock()
go func() {
time.Sleep(time.Duration(w.addons.Scheduler.TimeoutMillis) * time.Millisecond)
w.Scheduler.TimeoutLock.Lock()
w.Scheduler.TimeoutCounter--
if w.Scheduler.TimeoutCounter == 0 {
w.Scheduler.Trigger <- 0 // send when the latest buffer timeout
}
w.Scheduler.TimeoutLock.Unlock()
}()
}
if len(w.scheduler.Error) > 0 {
return <-w.scheduler.Error
if len(w.Scheduler.Error) > 0 {
return <-w.Scheduler.Error
}
return nil
}
Expand Down Expand Up @@ -518,7 +530,7 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
// CopyRawConnIfExist use the most efficient copy method.
// - If caller don't want to turn on splice, do not pass in both reader conn and writer conn
// - writer are from *transport.Link
func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net.Conn, writer buf.Writer, timer *signal.ActivityTimer, inTimer *signal.ActivityTimer) error {
func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net.Conn, writer buf.Writer, timer *signal.ActivityTimer, inTimer *signal.ActivityTimer, scheduler *Scheduler) error {
readerConn, readCounter, _ := UnwrapRawConn(readerConn)
writerConn, _, writeCounter := UnwrapRawConn(writerConn)
reader := buf.NewReader(readerConn)
Expand Down Expand Up @@ -581,10 +593,13 @@ func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net
if readCounter != nil {
readCounter.Add(int64(buffer.Len()))
}
timer.Update()
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
return werr
}
timer.Update()
}
if scheduler != nil {
scheduler.Trigger <- 2
}
if err != nil {
return err
Expand Down
25 changes: 23 additions & 2 deletions proxy/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type Scheduler struct {
Buffer chan buf.MultiBuffer
Trigger chan int
Error chan error
TimeoutCounter int
TimeoutLock *sync.Mutex
closed chan int
bufferReadLock *sync.Mutex
writer buf.Writer
Expand All @@ -24,11 +26,21 @@ type Scheduler struct {
ctx context.Context
}

func TriggerScheduler(scheduler *Scheduler) buf.CopyOption {
return func(handler *buf.CopyHandler) {
handler.OnData = append(handler.OnData, func(buf.MultiBuffer) {
scheduler.Trigger <- 2 // send fake buffer if no pending
})
}
}

func NewScheduler(w buf.Writer, addon *Addons, state *TrafficState, userUUID *[]byte, context context.Context) *Scheduler {
var s = Scheduler{
Buffer: make(chan buf.MultiBuffer, 100),
Trigger: make(chan int),
Error: make(chan error, 100),
TimeoutCounter: 0,
TimeoutLock: new(sync.Mutex),
closed: make(chan int),
bufferReadLock: new(sync.Mutex),
writer: w,
Expand All @@ -37,18 +49,27 @@ func NewScheduler(w buf.Writer, addon *Addons, state *TrafficState, userUUID *[]
writeOnceUserUUID: userUUID,
ctx: context,
}
return &s
}

func(s *Scheduler) Start() {
go s.mainLoop()
if s.addons.Scheduler != nil {
if s.addons.Scheduler != nil && !s.addons.Scheduler.PingPong {
go s.exampleIndependentScheduler()
}
return &s
}

func(s *Scheduler) mainLoop() {
for trigger := range s.Trigger {
if len(s.closed) > 0 {
return
}
if trigger == -1 && s.addons.Scheduler != nil {
continue
}
if trigger == 2 && (s.addons.Scheduler == nil || !s.addons.Scheduler.PingPong) {
continue
}
go func() { // each trigger has independent delay, trigger does not block
var d = 0 * time.Millisecond
if s.addons.Delay != nil {
Expand Down
16 changes: 5 additions & 11 deletions proxy/vless/encoding/addons.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"io"
"strings"

"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
Expand Down Expand Up @@ -54,15 +55,6 @@ func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*proxy.Addons, er
return addons, nil
}

// EncodeBodyAddons returns a Writer that auto-encrypt content written by caller.
func EncodeBodyAddons(writer buf.Writer, request *protocol.RequestHeader, addons *proxy.Addons, state *proxy.TrafficState, context context.Context) buf.Writer {
w := proxy.NewVisionWriter(writer, addons, state, context)
if request.Command == protocol.RequestCommandUDP {
return NewMultiLengthPacketWriter(w)
}
return w
}

// DecodeBodyAddons returns a Reader from which caller can fetch decrypted body.
func DecodeBodyAddons(reader io.Reader, request *protocol.RequestHeader, addons *proxy.Addons, state *proxy.TrafficState, context context.Context) buf.Reader {
r := proxy.NewVisionReader(buf.NewReader(reader), addons, state, context)
Expand Down Expand Up @@ -181,7 +173,7 @@ func (r *LengthPacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
func PopulateSeed(seed string, addons *proxy.Addons) {
if len(seed) > 0 {
addons.Seed = []byte {1} // only turn on, more TBD
addons.Mode = proxy.SeedMode_PaddingPlusDelay
addons.Mode = proxy.SeedMode_IndependentScheduler
addons.Duration = "0-8"
addons.Padding = &proxy.PaddingConfig{
RegularMin: 0,
Expand All @@ -196,6 +188,7 @@ func PopulateSeed(seed string, addons *proxy.Addons) {
// }
addons.Scheduler = &proxy.SchedulerConfig{
TimeoutMillis: 600,
PingPong: strings.Contains(seed, "pingpong"),
}
} else if addons.Flow == vless.XRV {
addons.Seed = []byte {1} // only turn on, more TBD
Expand Down Expand Up @@ -244,7 +237,8 @@ func CheckSeed(requestAddons *proxy.Addons, responseAddons *proxy.Addons) error
return errors.New("Delay of one is nil but the other is not nil")
}
if requestAddons.Scheduler != nil && responseAddons.Scheduler != nil {
if requestAddons.Scheduler.TimeoutMillis != responseAddons.Scheduler.TimeoutMillis {
if requestAddons.Scheduler.TimeoutMillis != responseAddons.Scheduler.TimeoutMillis ||
requestAddons.Scheduler.PingPong != responseAddons.Scheduler.PingPong {
return errors.New("Scheduler not match")
}
} else if requestAddons.Scheduler != nil || responseAddons.Scheduler != nil {
Expand Down
Loading

0 comments on commit 7743a2b

Please sign in to comment.