mirror of https://github.com/v2ray/v2ray-core
				
				
				
			
		
			
				
	
	
		
			130 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
package point
 | 
						|
 | 
						|
import (
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/v2ray/v2ray-core/app"
 | 
						|
	"github.com/v2ray/v2ray-core/common/dice"
 | 
						|
	"github.com/v2ray/v2ray-core/common/log"
 | 
						|
	v2net "github.com/v2ray/v2ray-core/common/net"
 | 
						|
	"github.com/v2ray/v2ray-core/common/retry"
 | 
						|
	"github.com/v2ray/v2ray-core/proxy"
 | 
						|
	proxyrepo "github.com/v2ray/v2ray-core/proxy/repo"
 | 
						|
)
 | 
						|
 | 
						|
type InboundDetourHandlerDynamic struct {
 | 
						|
	sync.RWMutex
 | 
						|
	space       app.Space
 | 
						|
	config      *InboundDetourConfig
 | 
						|
	portsInUse  map[v2net.Port]bool
 | 
						|
	ichInUse    []proxy.InboundHandler
 | 
						|
	ich2Recycle []proxy.InboundHandler
 | 
						|
	lastRefresh time.Time
 | 
						|
}
 | 
						|
 | 
						|
func NewInboundDetourHandlerDynamic(space app.Space, config *InboundDetourConfig) (*InboundDetourHandlerDynamic, error) {
 | 
						|
	handler := &InboundDetourHandlerDynamic{
 | 
						|
		space:      space,
 | 
						|
		config:     config,
 | 
						|
		portsInUse: make(map[v2net.Port]bool),
 | 
						|
	}
 | 
						|
	ichCount := config.Allocation.Concurrency
 | 
						|
	ichArray := make([]proxy.InboundHandler, ichCount*2)
 | 
						|
	for idx := range ichArray {
 | 
						|
		ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, config.Settings)
 | 
						|
		if err != nil {
 | 
						|
			log.Error("Point: Failed to create inbound connection handler: ", err)
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		ichArray[idx] = ich
 | 
						|
	}
 | 
						|
	handler.ichInUse = ichArray[:ichCount]
 | 
						|
	handler.ich2Recycle = ichArray[ichCount:]
 | 
						|
	return handler, nil
 | 
						|
}
 | 
						|
 | 
						|
func (this *InboundDetourHandlerDynamic) pickUnusedPort() v2net.Port {
 | 
						|
	delta := int(this.config.PortRange.To) - int(this.config.PortRange.From) + 1
 | 
						|
	for {
 | 
						|
		r := dice.Roll(delta)
 | 
						|
		port := this.config.PortRange.From + v2net.Port(r)
 | 
						|
		_, used := this.portsInUse[port]
 | 
						|
		if !used {
 | 
						|
			return port
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (this *InboundDetourHandlerDynamic) GetConnectionHandler() (proxy.InboundHandler, int) {
 | 
						|
	this.RLock()
 | 
						|
	defer this.RUnlock()
 | 
						|
	ich := this.ichInUse[dice.Roll(len(this.ichInUse))]
 | 
						|
	until := this.config.Allocation.Refresh - int((time.Now().Unix()-this.lastRefresh.Unix())/60/1000)
 | 
						|
	if until < 0 {
 | 
						|
		until = 0
 | 
						|
	}
 | 
						|
	return ich, int(until)
 | 
						|
}
 | 
						|
 | 
						|
func (this *InboundDetourHandlerDynamic) Close() {
 | 
						|
	this.Lock()
 | 
						|
	defer this.Unlock()
 | 
						|
	for _, ich := range this.ichInUse {
 | 
						|
		ich.Close()
 | 
						|
	}
 | 
						|
	if this.ich2Recycle != nil {
 | 
						|
		for _, ich := range this.ich2Recycle {
 | 
						|
			if ich != nil {
 | 
						|
				ich.Close()
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (this *InboundDetourHandlerDynamic) refresh() error {
 | 
						|
	this.lastRefresh = time.Now()
 | 
						|
 | 
						|
	for _, ich := range this.ich2Recycle {
 | 
						|
		port2Delete := ich.Port()
 | 
						|
 | 
						|
		ich.Close()
 | 
						|
		err := retry.Timed(100 /* times */, 1000 /* ms */).On(func() error {
 | 
						|
			port := this.pickUnusedPort()
 | 
						|
			err := ich.Listen(port)
 | 
						|
			if err != nil {
 | 
						|
				log.Error("Point: Failed to start inbound detour on port ", port, ": ", err)
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			this.portsInUse[port] = true
 | 
						|
			return nil
 | 
						|
		})
 | 
						|
		if err != nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		delete(this.portsInUse, port2Delete)
 | 
						|
	}
 | 
						|
 | 
						|
	this.Lock()
 | 
						|
	this.ich2Recycle, this.ichInUse = this.ichInUse, this.ich2Recycle
 | 
						|
	this.Unlock()
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (this *InboundDetourHandlerDynamic) Start() error {
 | 
						|
	err := this.refresh()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	go func() {
 | 
						|
		for range time.Tick(time.Duration(this.config.Allocation.Refresh) * time.Minute) {
 | 
						|
			this.refresh()
 | 
						|
		}
 | 
						|
	}()
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |