github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/app/reverse/bridge.go (about) 1 // +build !confonly 2 3 package reverse 4 5 import ( 6 "context" 7 "time" 8 9 "github.com/golang/protobuf/proto" 10 "v2ray.com/core/common/mux" 11 "v2ray.com/core/common/net" 12 "v2ray.com/core/common/session" 13 "v2ray.com/core/common/task" 14 "v2ray.com/core/features/routing" 15 "v2ray.com/core/transport" 16 "v2ray.com/core/transport/pipe" 17 ) 18 19 // Bridge is a component in reverse proxy, that relays connections from Portal to local address. 20 type Bridge struct { 21 dispatcher routing.Dispatcher 22 tag string 23 domain string 24 workers []*BridgeWorker 25 monitorTask *task.Periodic 26 } 27 28 // NewBridge creates a new Bridge instance. 29 func NewBridge(config *BridgeConfig, dispatcher routing.Dispatcher) (*Bridge, error) { 30 if config.Tag == "" { 31 return nil, newError("bridge tag is empty") 32 } 33 if config.Domain == "" { 34 return nil, newError("bridge domain is empty") 35 } 36 37 b := &Bridge{ 38 dispatcher: dispatcher, 39 tag: config.Tag, 40 domain: config.Domain, 41 } 42 b.monitorTask = &task.Periodic{ 43 Execute: b.monitor, 44 Interval: time.Second * 2, 45 } 46 return b, nil 47 } 48 49 func (b *Bridge) cleanup() { 50 var activeWorkers []*BridgeWorker 51 52 for _, w := range b.workers { 53 if w.IsActive() { 54 activeWorkers = append(activeWorkers, w) 55 } 56 } 57 58 if len(activeWorkers) != len(b.workers) { 59 b.workers = activeWorkers 60 } 61 } 62 63 func (b *Bridge) monitor() error { 64 b.cleanup() 65 66 var numConnections uint32 67 var numWorker uint32 68 69 for _, w := range b.workers { 70 if w.IsActive() { 71 numConnections += w.Connections() 72 numWorker++ 73 } 74 } 75 76 if numWorker == 0 || numConnections/numWorker > 16 { 77 worker, err := NewBridgeWorker(b.domain, b.tag, b.dispatcher) 78 if err != nil { 79 newError("failed to create bridge worker").Base(err).AtWarning().WriteToLog() 80 return nil 81 } 82 b.workers = append(b.workers, worker) 83 } 84 85 return nil 86 } 87 88 func (b *Bridge) Start() error { 89 return b.monitorTask.Start() 90 } 91 92 func (b *Bridge) Close() error { 93 return b.monitorTask.Close() 94 } 95 96 type BridgeWorker struct { 97 tag string 98 worker *mux.ServerWorker 99 dispatcher routing.Dispatcher 100 state Control_State 101 } 102 103 func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) { 104 ctx := context.Background() 105 ctx = session.ContextWithInbound(ctx, &session.Inbound{ 106 Tag: tag, 107 }) 108 link, err := d.Dispatch(ctx, net.Destination{ 109 Network: net.Network_TCP, 110 Address: net.DomainAddress(domain), 111 Port: 0, 112 }) 113 if err != nil { 114 return nil, err 115 } 116 117 w := &BridgeWorker{ 118 dispatcher: d, 119 tag: tag, 120 } 121 122 worker, err := mux.NewServerWorker(context.Background(), w, link) 123 if err != nil { 124 return nil, err 125 } 126 w.worker = worker 127 128 return w, nil 129 } 130 131 func (w *BridgeWorker) Type() interface{} { 132 return routing.DispatcherType() 133 } 134 135 func (w *BridgeWorker) Start() error { 136 return nil 137 } 138 139 func (w *BridgeWorker) Close() error { 140 return nil 141 } 142 143 func (w *BridgeWorker) IsActive() bool { 144 return w.state == Control_ACTIVE && !w.worker.Closed() 145 } 146 147 func (w *BridgeWorker) Connections() uint32 { 148 return w.worker.ActiveConnections() 149 } 150 151 func (w *BridgeWorker) handleInternalConn(link transport.Link) { 152 go func() { 153 reader := link.Reader 154 for { 155 mb, err := reader.ReadMultiBuffer() 156 if err != nil { 157 break 158 } 159 for _, b := range mb { 160 var ctl Control 161 if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil { 162 newError("failed to parse proto message").Base(err).WriteToLog() 163 break 164 } 165 if ctl.State != w.state { 166 w.state = ctl.State 167 } 168 } 169 } 170 }() 171 } 172 173 func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { 174 if !isInternalDomain(dest) { 175 ctx = session.ContextWithInbound(ctx, &session.Inbound{ 176 Tag: w.tag, 177 }) 178 return w.dispatcher.Dispatch(ctx, dest) 179 } 180 181 opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)} 182 uplinkReader, uplinkWriter := pipe.New(opt...) 183 downlinkReader, downlinkWriter := pipe.New(opt...) 184 185 w.handleInternalConn(transport.Link{ 186 Reader: downlinkReader, 187 Writer: uplinkWriter, 188 }) 189 190 return &transport.Link{ 191 Reader: uplinkReader, 192 Writer: downlinkWriter, 193 }, nil 194 }