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 }