github.com/eagleql/xray-core@v1.4.4/app/proxyman/inbound/dynamic.go (about)

     1  package inbound
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/eagleql/xray-core/app/proxyman"
     9  	"github.com/eagleql/xray-core/common/dice"
    10  	"github.com/eagleql/xray-core/common/mux"
    11  	"github.com/eagleql/xray-core/common/net"
    12  	"github.com/eagleql/xray-core/common/task"
    13  	"github.com/eagleql/xray-core/core"
    14  	"github.com/eagleql/xray-core/proxy"
    15  	"github.com/eagleql/xray-core/transport/internet"
    16  )
    17  
    18  type DynamicInboundHandler struct {
    19  	tag            string
    20  	v              *core.Instance
    21  	proxyConfig    interface{}
    22  	receiverConfig *proxyman.ReceiverConfig
    23  	streamSettings *internet.MemoryStreamConfig
    24  	portMutex      sync.Mutex
    25  	portsInUse     map[net.Port]bool
    26  	workerMutex    sync.RWMutex
    27  	worker         []worker
    28  	lastRefresh    time.Time
    29  	mux            *mux.Server
    30  	task           *task.Periodic
    31  
    32  	ctx context.Context
    33  }
    34  
    35  func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) {
    36  	v := core.MustFromContext(ctx)
    37  	h := &DynamicInboundHandler{
    38  		tag:            tag,
    39  		proxyConfig:    proxyConfig,
    40  		receiverConfig: receiverConfig,
    41  		portsInUse:     make(map[net.Port]bool),
    42  		mux:            mux.NewServer(ctx),
    43  		v:              v,
    44  		ctx:            ctx,
    45  	}
    46  
    47  	mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings)
    48  	if err != nil {
    49  		return nil, newError("failed to parse stream settings").Base(err).AtWarning()
    50  	}
    51  	if receiverConfig.ReceiveOriginalDestination {
    52  		if mss.SocketSettings == nil {
    53  			mss.SocketSettings = &internet.SocketConfig{}
    54  		}
    55  		if mss.SocketSettings.Tproxy == internet.SocketConfig_Off {
    56  			mss.SocketSettings.Tproxy = internet.SocketConfig_Redirect
    57  		}
    58  		mss.SocketSettings.ReceiveOriginalDestAddress = true
    59  	}
    60  
    61  	h.streamSettings = mss
    62  
    63  	h.task = &task.Periodic{
    64  		Interval: time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()),
    65  		Execute:  h.refresh,
    66  	}
    67  
    68  	return h, nil
    69  }
    70  
    71  func (h *DynamicInboundHandler) allocatePort() net.Port {
    72  	from := int(h.receiverConfig.PortRange.From)
    73  	delta := int(h.receiverConfig.PortRange.To) - from + 1
    74  
    75  	h.portMutex.Lock()
    76  	defer h.portMutex.Unlock()
    77  
    78  	for {
    79  		r := dice.Roll(delta)
    80  		port := net.Port(from + r)
    81  		_, used := h.portsInUse[port]
    82  		if !used {
    83  			h.portsInUse[port] = true
    84  			return port
    85  		}
    86  	}
    87  }
    88  
    89  func (h *DynamicInboundHandler) closeWorkers(workers []worker) {
    90  	ports2Del := make([]net.Port, len(workers))
    91  	for idx, worker := range workers {
    92  		ports2Del[idx] = worker.Port()
    93  		if err := worker.Close(); err != nil {
    94  			newError("failed to close worker").Base(err).WriteToLog()
    95  		}
    96  	}
    97  
    98  	h.portMutex.Lock()
    99  	for _, port := range ports2Del {
   100  		delete(h.portsInUse, port)
   101  	}
   102  	h.portMutex.Unlock()
   103  }
   104  
   105  func (h *DynamicInboundHandler) refresh() error {
   106  	h.lastRefresh = time.Now()
   107  
   108  	timeout := time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()) * 2
   109  	concurrency := h.receiverConfig.AllocationStrategy.GetConcurrencyValue()
   110  	workers := make([]worker, 0, concurrency)
   111  
   112  	address := h.receiverConfig.Listen.AsAddress()
   113  	if address == nil {
   114  		address = net.AnyIP
   115  	}
   116  
   117  	uplinkCounter, downlinkCounter := getStatCounter(h.v, h.tag)
   118  
   119  	for i := uint32(0); i < concurrency; i++ {
   120  		port := h.allocatePort()
   121  		rawProxy, err := core.CreateObject(h.v, h.proxyConfig)
   122  		if err != nil {
   123  			newError("failed to create proxy instance").Base(err).AtWarning().WriteToLog()
   124  			continue
   125  		}
   126  		p := rawProxy.(proxy.Inbound)
   127  		nl := p.Network()
   128  		if net.HasNetwork(nl, net.Network_TCP) {
   129  			worker := &tcpWorker{
   130  				tag:             h.tag,
   131  				address:         address,
   132  				port:            port,
   133  				proxy:           p,
   134  				stream:          h.streamSettings,
   135  				recvOrigDest:    h.receiverConfig.ReceiveOriginalDestination,
   136  				dispatcher:      h.mux,
   137  				sniffingConfig:  h.receiverConfig.GetEffectiveSniffingSettings(),
   138  				uplinkCounter:   uplinkCounter,
   139  				downlinkCounter: downlinkCounter,
   140  				ctx:             h.ctx,
   141  			}
   142  			if err := worker.Start(); err != nil {
   143  				newError("failed to create TCP worker").Base(err).AtWarning().WriteToLog()
   144  				continue
   145  			}
   146  			workers = append(workers, worker)
   147  		}
   148  
   149  		if net.HasNetwork(nl, net.Network_UDP) {
   150  			worker := &udpWorker{
   151  				tag:             h.tag,
   152  				proxy:           p,
   153  				address:         address,
   154  				port:            port,
   155  				dispatcher:      h.mux,
   156  				sniffingConfig:  h.receiverConfig.GetEffectiveSniffingSettings(),
   157  				uplinkCounter:   uplinkCounter,
   158  				downlinkCounter: downlinkCounter,
   159  				stream:          h.streamSettings,
   160  				ctx:             h.ctx,
   161  			}
   162  			if err := worker.Start(); err != nil {
   163  				newError("failed to create UDP worker").Base(err).AtWarning().WriteToLog()
   164  				continue
   165  			}
   166  			workers = append(workers, worker)
   167  		}
   168  	}
   169  
   170  	h.workerMutex.Lock()
   171  	h.worker = workers
   172  	h.workerMutex.Unlock()
   173  
   174  	time.AfterFunc(timeout, func() {
   175  		h.closeWorkers(workers)
   176  	})
   177  
   178  	return nil
   179  }
   180  
   181  func (h *DynamicInboundHandler) Start() error {
   182  	return h.task.Start()
   183  }
   184  
   185  func (h *DynamicInboundHandler) Close() error {
   186  	return h.task.Close()
   187  }
   188  
   189  func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) {
   190  	h.workerMutex.RLock()
   191  	defer h.workerMutex.RUnlock()
   192  
   193  	if len(h.worker) == 0 {
   194  		return nil, 0, 0
   195  	}
   196  	w := h.worker[dice.Roll(len(h.worker))]
   197  	expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute)
   198  	return w.Proxy(), w.Port(), int(expire)
   199  }
   200  
   201  func (h *DynamicInboundHandler) Tag() string {
   202  	return h.tag
   203  }