github.com/EagleQL/Xray-core@v1.4.3/app/proxyman/inbound/dynamic.go (about) 1 package inbound 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/xtls/xray-core/app/proxyman" 9 "github.com/xtls/xray-core/common/dice" 10 "github.com/xtls/xray-core/common/mux" 11 "github.com/xtls/xray-core/common/net" 12 "github.com/xtls/xray-core/common/task" 13 "github.com/xtls/xray-core/core" 14 "github.com/xtls/xray-core/proxy" 15 "github.com/xtls/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 }