github.com/v2fly/v2ray-core/v4@v4.45.2/app/reverse/bridge.go (about)

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