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 }