github.com/moqsien/xraycore@v1.8.5/proxy/loopback/loopback.go (about)

     1  package loopback
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/moqsien/xraycore/common"
     7  	"github.com/moqsien/xraycore/common/buf"
     8  	"github.com/moqsien/xraycore/common/net"
     9  	"github.com/moqsien/xraycore/common/net/cnc"
    10  	"github.com/moqsien/xraycore/common/retry"
    11  	"github.com/moqsien/xraycore/common/session"
    12  	"github.com/moqsien/xraycore/common/task"
    13  	"github.com/moqsien/xraycore/core"
    14  	"github.com/moqsien/xraycore/features/routing"
    15  	"github.com/moqsien/xraycore/transport"
    16  	"github.com/moqsien/xraycore/transport/internet"
    17  )
    18  
    19  type Loopback struct {
    20  	config             *Config
    21  	dispatcherInstance routing.Dispatcher
    22  }
    23  
    24  func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet.Dialer) error {
    25  	outbound := session.OutboundFromContext(ctx)
    26  	if outbound == nil || !outbound.Target.IsValid() {
    27  		return newError("target not specified.")
    28  	}
    29  	destination := outbound.Target
    30  
    31  	newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))
    32  
    33  	input := link.Reader
    34  	output := link.Writer
    35  
    36  	var conn net.Conn
    37  	err := retry.ExponentialBackoff(2, 100).On(func() error {
    38  		dialDest := destination
    39  
    40  		content := new(session.Content)
    41  		content.SkipDNSResolve = true
    42  
    43  		ctx = session.ContextWithContent(ctx, content)
    44  
    45  		inbound := session.InboundFromContext(ctx)
    46  
    47  		inbound.Tag = l.config.InboundTag
    48  
    49  		ctx = session.ContextWithInbound(ctx, inbound)
    50  
    51  		rawConn, err := l.dispatcherInstance.Dispatch(ctx, dialDest)
    52  		if err != nil {
    53  			return err
    54  		}
    55  
    56  		var readerOpt cnc.ConnectionOption
    57  		if dialDest.Network == net.Network_TCP {
    58  			readerOpt = cnc.ConnectionOutputMulti(rawConn.Reader)
    59  		} else {
    60  			readerOpt = cnc.ConnectionOutputMultiUDP(rawConn.Reader)
    61  		}
    62  
    63  		conn = cnc.NewConnection(cnc.ConnectionInputMulti(rawConn.Writer), readerOpt)
    64  		return nil
    65  	})
    66  	if err != nil {
    67  		return newError("failed to open connection to ", destination).Base(err)
    68  	}
    69  	defer conn.Close()
    70  
    71  	requestDone := func() error {
    72  		var writer buf.Writer
    73  		if destination.Network == net.Network_TCP {
    74  			writer = buf.NewWriter(conn)
    75  		} else {
    76  			writer = &buf.SequentialWriter{Writer: conn}
    77  		}
    78  
    79  		if err := buf.Copy(input, writer); err != nil {
    80  			return newError("failed to process request").Base(err)
    81  		}
    82  
    83  		return nil
    84  	}
    85  
    86  	responseDone := func() error {
    87  		var reader buf.Reader
    88  		if destination.Network == net.Network_TCP {
    89  			reader = buf.NewReader(conn)
    90  		} else {
    91  			reader = buf.NewPacketReader(conn)
    92  		}
    93  		if err := buf.Copy(reader, output); err != nil {
    94  			return newError("failed to process response").Base(err)
    95  		}
    96  
    97  		return nil
    98  	}
    99  
   100  	if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
   101  		return newError("connection ends").Base(err)
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  func (l *Loopback) init(config *Config, dispatcherInstance routing.Dispatcher) error {
   108  	l.dispatcherInstance = dispatcherInstance
   109  	l.config = config
   110  	return nil
   111  }
   112  
   113  func init() {
   114  	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   115  		l := new(Loopback)
   116  		err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) error {
   117  			return l.init(config.(*Config), dispatcherInstance)
   118  		})
   119  		return l, err
   120  	}))
   121  }