github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/reverse/bridge.go (about)

     1  package reverse
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"google.golang.org/protobuf/proto"
     8  
     9  	"github.com/v2fly/v2ray-core/v5/common/mux"
    10  	"github.com/v2fly/v2ray-core/v5/common/net"
    11  	"github.com/v2fly/v2ray-core/v5/common/session"
    12  	"github.com/v2fly/v2ray-core/v5/common/task"
    13  	"github.com/v2fly/v2ray-core/v5/features/routing"
    14  	"github.com/v2fly/v2ray-core/v5/transport"
    15  	"github.com/v2fly/v2ray-core/v5/transport/pipe"
    16  )
    17  
    18  // Bridge is a component in reverse proxy, that relays connections from Portal to local address.
    19  type Bridge struct {
    20  	ctx         context.Context
    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(ctx context.Context, 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  		ctx:        ctx,
    39  		dispatcher: dispatcher,
    40  		tag:        config.Tag,
    41  		domain:     config.Domain,
    42  	}
    43  	b.monitorTask = &task.Periodic{
    44  		Execute:  b.monitor,
    45  		Interval: time.Second * 2,
    46  	}
    47  	return b, nil
    48  }
    49  
    50  func (b *Bridge) cleanup() {
    51  	var activeWorkers []*BridgeWorker
    52  
    53  	for _, w := range b.workers {
    54  		if w.IsActive() {
    55  			activeWorkers = append(activeWorkers, w)
    56  		}
    57  	}
    58  
    59  	if len(activeWorkers) != len(b.workers) {
    60  		b.workers = activeWorkers
    61  	}
    62  }
    63  
    64  func (b *Bridge) monitor() error {
    65  	b.cleanup()
    66  
    67  	var numConnections uint32
    68  	var numWorker uint32
    69  
    70  	for _, w := range b.workers {
    71  		if w.IsActive() {
    72  			numConnections += w.Connections()
    73  			numWorker++
    74  		}
    75  	}
    76  
    77  	if numWorker == 0 || numConnections/numWorker > 16 {
    78  		worker, err := NewBridgeWorker(b.ctx, b.domain, b.tag, b.dispatcher)
    79  		if err != nil {
    80  			newError("failed to create bridge worker").Base(err).AtWarning().WriteToLog()
    81  			return nil
    82  		}
    83  		b.workers = append(b.workers, worker)
    84  	}
    85  
    86  	return nil
    87  }
    88  
    89  func (b *Bridge) Start() error {
    90  	return b.monitorTask.Start()
    91  }
    92  
    93  func (b *Bridge) Close() error {
    94  	return b.monitorTask.Close()
    95  }
    96  
    97  type BridgeWorker struct {
    98  	tag        string
    99  	worker     *mux.ServerWorker
   100  	dispatcher routing.Dispatcher
   101  	state      Control_State
   102  }
   103  
   104  func NewBridgeWorker(ctx context.Context, domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) {
   105  	bridgeCtx := session.ContextWithInbound(ctx, &session.Inbound{
   106  		Tag: tag,
   107  	})
   108  	link, err := d.Dispatch(bridgeCtx, 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(ctx, 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  }