github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/proxy/loopback/loopback.go (about)

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