| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 | package coreimport (	"context"	"sync"	"time"	"v2ray.com/core/app"	"v2ray.com/core/common/dice"	"v2ray.com/core/common/log"	"v2ray.com/core/common/net"	"v2ray.com/core/common/retry"	"v2ray.com/core/proxy")type InboundDetourHandlerDynamic struct {	sync.RWMutex	space       app.Space	config      *InboundConnectionConfig	portsInUse  map[net.Port]bool	ichs        []proxy.InboundHandler	ich2Recyle  []proxy.InboundHandler	lastRefresh time.Time	ctx         context.Context}func NewInboundDetourHandlerDynamic(ctx context.Context, config *InboundConnectionConfig) (*InboundDetourHandlerDynamic, error) {	space := app.SpaceFromContext(ctx)	handler := &InboundDetourHandlerDynamic{		space:      space,		config:     config,		portsInUse: make(map[net.Port]bool),		ctx:        ctx,	}	handler.ichs = make([]proxy.InboundHandler, config.GetAllocationStrategyValue().GetConcurrencyValue())	// To test configuration	ichConfig, err := config.GetTypedSettings()	if err != nil {		return nil, err	}	ich, err := proxy.CreateInboundHandler(proxy.ContextWithInboundMeta(ctx, &proxy.InboundHandlerMeta{		Address:                config.GetListenOnValue(),		Port:                   0,		Tag:                    config.Tag,		StreamSettings:         config.StreamSettings,		AllowPassiveConnection: config.AllowPassiveConnection,	}), ichConfig)	if err != nil {		log.Error("Point: Failed to create inbound connection handler: ", err)		return nil, err	}	ich.Close()	return handler, nil}func (v *InboundDetourHandlerDynamic) pickUnusedPort() net.Port {	delta := int(v.config.PortRange.To) - int(v.config.PortRange.From) + 1	for {		r := dice.Roll(delta)		port := v.config.PortRange.FromPort() + net.Port(r)		_, used := v.portsInUse[port]		if !used {			return port		}	}}func (v *InboundDetourHandlerDynamic) GetConnectionHandler() (proxy.InboundHandler, int) {	v.RLock()	defer v.RUnlock()	ich := v.ichs[dice.Roll(len(v.ichs))]	until := int(v.config.GetAllocationStrategyValue().GetRefreshValue()) - int((time.Now().Unix()-v.lastRefresh.Unix())/60/1000)	if until < 0 {		until = 0	}	return ich, int(until)}func (v *InboundDetourHandlerDynamic) Close() {	v.Lock()	defer v.Unlock()	for _, ich := range v.ichs {		ich.Close()	}}func (v *InboundDetourHandlerDynamic) RecyleHandles() {	if v.ich2Recyle != nil {		for _, ich := range v.ich2Recyle {			if ich == nil {				continue			}			port := ich.Port()			ich.Close()			delete(v.portsInUse, port)		}		v.ich2Recyle = nil	}}func (v *InboundDetourHandlerDynamic) refresh() error {	v.lastRefresh = time.Now()	config := v.config	v.ich2Recyle = v.ichs	newIchs := make([]proxy.InboundHandler, config.GetAllocationStrategyValue().GetConcurrencyValue())	for idx := range newIchs {		err := retry.Timed(5, 100).On(func() error {			port := v.pickUnusedPort()			ichConfig, _ := config.GetTypedSettings()			ich, err := proxy.CreateInboundHandler(proxy.ContextWithInboundMeta(v.ctx, &proxy.InboundHandlerMeta{				Address: config.GetListenOnValue(),				Port:    port, Tag: config.Tag,				StreamSettings: config.StreamSettings}), ichConfig)			if err != nil {				delete(v.portsInUse, port)				return err			}			err = ich.Start()			if err != nil {				delete(v.portsInUse, port)				return err			}			v.portsInUse[port] = true			newIchs[idx] = ich			return nil		})		if err != nil {			log.Error("Point: Failed to create inbound connection handler: ", err)			return err		}	}	v.Lock()	v.ichs = newIchs	v.Unlock()	return nil}func (v *InboundDetourHandlerDynamic) Start() error {	err := v.refresh()	if err != nil {		log.Error("Point: Failed to refresh dynamic allocations: ", err)		return err	}	go func() {		for {			time.Sleep(time.Duration(v.config.GetAllocationStrategyValue().GetRefreshValue())*time.Minute - 1)			v.RecyleHandles()			err := v.refresh()			if err != nil {				log.Error("Point: Failed to refresh dynamic allocations: ", err)			}			time.Sleep(time.Minute)		}	}()	return nil}
 |