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  }