github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/proxy/loopback/loopback.go (about) 1 package loopback 2 3 import ( 4 "context" 5 6 "github.com/xtls/xray-core/common" 7 "github.com/xtls/xray-core/common/buf" 8 "github.com/xtls/xray-core/common/net" 9 "github.com/xtls/xray-core/common/net/cnc" 10 "github.com/xtls/xray-core/common/retry" 11 "github.com/xtls/xray-core/common/session" 12 "github.com/xtls/xray-core/common/task" 13 "github.com/xtls/xray-core/core" 14 "github.com/xtls/xray-core/features/routing" 15 "github.com/xtls/xray-core/transport" 16 "github.com/xtls/xray-core/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 outbounds := session.OutboundsFromContext(ctx) 26 ob := outbounds[len(outbounds) - 1] 27 if !ob.Target.IsValid() { 28 return newError("target not specified.") 29 } 30 ob.Name = "loopback" 31 destination := ob.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 net.Conn 39 err := retry.ExponentialBackoff(2, 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 cnc.ConnectionOption 59 if dialDest.Network == net.Network_TCP { 60 readerOpt = cnc.ConnectionOutputMulti(rawConn.Reader) 61 } else { 62 readerOpt = cnc.ConnectionOutputMultiUDP(rawConn.Reader) 63 } 64 65 conn = cnc.NewConnection(cnc.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 }