Add new admin panel, failover, dns fallback, providers, limiters. Update XHTTP

This commit is contained in:
Sergei Maklagin
2026-05-11 00:59:35 +03:00
parent 652e0baf57
commit 3bd162ed6f
241 changed files with 36409 additions and 4086 deletions

View File

@@ -3,8 +3,6 @@ package bandwidth
import (
"context"
"net"
"golang.org/x/time/rate"
)
type connWithDownloadBandwidthLimiter struct {
@@ -13,7 +11,7 @@ type connWithDownloadBandwidthLimiter struct {
limiter Limiter
}
func NewConnWithDownloadBandwidthLimiter(ctx context.Context, conn net.Conn, limiter *rate.Limiter) *connWithDownloadBandwidthLimiter {
func NewConnWithDownloadBandwidthLimiter(ctx context.Context, conn net.Conn, limiter Limiter) *connWithDownloadBandwidthLimiter {
return &connWithDownloadBandwidthLimiter{conn, ctx, limiter}
}
@@ -28,12 +26,11 @@ func (conn *connWithDownloadBandwidthLimiter) Write(p []byte) (n int, err error)
type connWithUploadBandwidthLimiter struct {
net.Conn
ctx context.Context
limiter *rate.Limiter
burst int
limiter Limiter
}
func NewConnWithUploadBandwidthLimiter(ctx context.Context, conn net.Conn, limiter *rate.Limiter) *connWithUploadBandwidthLimiter {
return &connWithUploadBandwidthLimiter{conn, ctx, limiter, limiter.Burst()}
func NewConnWithUploadBandwidthLimiter(ctx context.Context, conn net.Conn, limiter Limiter) *connWithUploadBandwidthLimiter {
return &connWithUploadBandwidthLimiter{conn, ctx, limiter}
}
func (conn *connWithUploadBandwidthLimiter) Read(p []byte) (n int, err error) {
@@ -65,12 +62,11 @@ func (conn *connWithCloseHandler) Close() error {
type packetConnWithDownloadBandwidthLimiter struct {
net.PacketConn
ctx context.Context
limiter *rate.Limiter
burst int
limiter Limiter
}
func NewPacketConnWithDownloadBandwidthLimiter(ctx context.Context, conn net.PacketConn, limiter *rate.Limiter) *packetConnWithDownloadBandwidthLimiter {
return &packetConnWithDownloadBandwidthLimiter{conn, ctx, limiter, limiter.Burst()}
func NewPacketConnWithDownloadBandwidthLimiter(ctx context.Context, conn net.PacketConn, limiter Limiter) *packetConnWithDownloadBandwidthLimiter {
return &packetConnWithDownloadBandwidthLimiter{conn, ctx, limiter}
}
func (conn *packetConnWithDownloadBandwidthLimiter) WriteTo(p []byte, addr net.Addr) (n int, err error) {
@@ -85,11 +81,10 @@ type packetConnWithUploadBandwidthLimiter struct {
net.PacketConn
ctx context.Context
limiter Limiter
burst int
}
func NewPacketConnWithUploadBandwidthLimiter(ctx context.Context, conn net.PacketConn, limiter *rate.Limiter) *packetConnWithUploadBandwidthLimiter {
return &packetConnWithUploadBandwidthLimiter{conn, ctx, limiter, limiter.Burst()}
func NewPacketConnWithUploadBandwidthLimiter(ctx context.Context, conn net.PacketConn, limiter Limiter) *packetConnWithUploadBandwidthLimiter {
return &packetConnWithUploadBandwidthLimiter{conn, ctx, limiter}
}
func (conn *packetConnWithUploadBandwidthLimiter) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
@@ -117,3 +112,39 @@ func (conn *packetConnWithCloseHandler) Close() error {
conn.onClose()
return conn.PacketConn.Close()
}
func connWithDownloadBandwidthWrapper(ctx context.Context, conn net.Conn, limiter Limiter, reverse bool) net.Conn {
if reverse {
return NewConnWithUploadBandwidthLimiter(ctx, conn, limiter)
}
return NewConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
}
func connWithUploadBandwidthWrapper(ctx context.Context, conn net.Conn, limiter Limiter, reverse bool) net.Conn {
if reverse {
return NewConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
}
return NewConnWithUploadBandwidthLimiter(ctx, conn, limiter)
}
func connWithBidirectionalBandwidthWrapper(ctx context.Context, conn net.Conn, limiter Limiter, reverse bool) net.Conn {
return NewConnWithUploadBandwidthLimiter(ctx, NewConnWithDownloadBandwidthLimiter(ctx, conn, limiter), limiter)
}
func packetConnWithDownloadBandwidthWrapper(ctx context.Context, conn net.PacketConn, limiter Limiter, reverse bool) net.PacketConn {
if reverse {
return NewPacketConnWithUploadBandwidthLimiter(ctx, conn, limiter)
}
return NewPacketConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
}
func packetConnWithUploadBandwidthWrapper(ctx context.Context, conn net.PacketConn, limiter Limiter, reverse bool) net.PacketConn {
if reverse {
return NewPacketConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
}
return NewPacketConnWithUploadBandwidthLimiter(ctx, conn, limiter)
}
func packetConnWithBidirectionalBandwidthWrapper(ctx context.Context, conn net.PacketConn, limiter Limiter, reverse bool) net.PacketConn {
return NewPacketConnWithUploadBandwidthLimiter(ctx, NewPacketConnWithDownloadBandwidthLimiter(ctx, conn, limiter), limiter)
}

View File

@@ -2,8 +2,130 @@ package bandwidth
import (
"context"
"slices"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
)
type Limiter interface {
WaitN(ctx context.Context, n int) (err error)
}
type FlowKeysLimiter struct {
limiter Limiter
connIDGetter ConnIDGetter
waits map[string][]*wait
conns map[string]int
queue chan struct{}
reset time.Time
mtx sync.Mutex
}
func NewFlowKeysLimiter(connIDGetter ConnIDGetter, limiter Limiter) *FlowKeysLimiter {
return &FlowKeysLimiter{
limiter: limiter,
connIDGetter: connIDGetter,
waits: make(map[string][]*wait),
conns: make(map[string]int),
queue: make(chan struct{}, 1),
reset: time.Now().Add(time.Second),
}
}
func (l *FlowKeysLimiter) WaitN(ctx context.Context, n int) error {
id, _ := l.connIDGetter(ctx, adapter.ContextFrom(ctx))
mainWait := &wait{ctx, make(chan struct{}), n}
l.mtx.Lock()
if waits, ok := l.waits[id]; ok {
l.waits[id] = append(waits, mainWait)
} else {
l.waits[id] = []*wait{mainWait}
}
l.mtx.Unlock()
select {
case l.queue <- struct{}{}:
case <-mainWait.finish:
return nil
case <-ctx.Done():
l.mtx.Lock()
for i, wait := range l.waits[id] {
if wait == mainWait {
l.waits[id] = slices.Delete(l.waits[id], i, i+1)
close(wait.finish)
break
}
}
l.mtx.Unlock()
return ctx.Err()
}
for {
if ctx.Err() != nil {
l.mtx.Lock()
for i, wait := range l.waits[id] {
if wait == mainWait {
l.waits[id] = slices.Delete(l.waits[id], i, i+1)
close(wait.finish)
break
}
}
l.mtx.Unlock()
<-l.queue
return ctx.Err()
}
now := time.Now()
if l.reset.Compare(now) == -1 {
clear(l.conns)
l.reset = now.Add(time.Second)
}
l.mtx.Lock()
var minConnId string
var minN int
for connID, waits := range l.waits {
if len(waits) == 0 {
continue
}
if n, ok := l.conns[connID]; ok {
if minConnId == "" {
minConnId = connID
minN = n
continue
}
if n+waits[0].n < minN {
minConnId = connID
minN = n
}
} else {
l.conns[connID] = 0
minConnId = connID
break
}
}
minWait := l.waits[minConnId][0]
l.waits[minConnId][0] = nil
l.waits[minConnId] = l.waits[minConnId][1:]
if len(l.waits) == 0 {
delete(l.waits, minConnId)
}
l.mtx.Unlock()
err := l.limiter.WaitN(ctx, minWait.n)
if err != nil {
continue
}
l.conns[minConnId] = l.conns[minConnId] + minWait.n
close(minWait.finish)
if minWait == mainWait {
<-l.queue
return nil
}
}
}
type wait struct {
ctx context.Context
finish chan struct{}
n int
}

View File

@@ -48,7 +48,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
case "users":
usersStrategies := make(map[string]BandwidthStrategy, len(options.Users))
for _, user := range options.Users {
userStrategy, err := CreateStrategy(user.Strategy, user.Mode, user.ConnectionType, options.Speed.Value())
userStrategy, err := CreateStrategy(user.Strategy, user.Mode, user.ConnectionType, options.Speed.Value(), options.FlowKeys)
if err != nil {
return nil, err
}
@@ -58,7 +58,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
case "manager":
strategy = NewManagerBandwidthStrategy()
default:
strategy, err = CreateStrategy(options.Strategy, options.Mode, options.ConnectionType, options.Speed.Value())
strategy, err = CreateStrategy(options.Strategy, options.Mode, options.ConnectionType, options.Speed.Value(), options.FlowKeys)
if err != nil {
return nil, err
}
@@ -118,9 +118,11 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
}
func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
ctx = adapter.WithContext(ctx, &metadata)
conn, err := h.strategy.wrapConn(ctx, conn, &metadata, false)
if err != nil {
h.logger.ErrorContext(ctx, err)
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
metadata.Inbound = h.Tag()
@@ -130,9 +132,11 @@ func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata
}
func (h *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
ctx = adapter.WithContext(ctx, &metadata)
packetConn, err := h.strategy.wrapPacketConn(ctx, bufio.NewNetPacketConn(conn), &metadata, false)
if err != nil {
h.logger.ErrorContext(ctx, err)
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
metadata.Inbound = h.Tag()

View File

@@ -15,8 +15,8 @@ import (
type (
CloseHandlerFunc = func()
ConnIDGetter = func(context.Context, *adapter.InboundContext) (string, bool)
ConnWrapper = func(ctx context.Context, conn net.Conn, limiter *rate.Limiter, reverse bool) net.Conn
PacketConnWrapper = func(ctx context.Context, conn net.PacketConn, limiter *rate.Limiter, reverse bool) net.PacketConn
ConnWrapper = func(ctx context.Context, conn net.Conn, limiter Limiter, reverse bool) net.Conn
PacketConnWrapper = func(ctx context.Context, conn net.PacketConn, limiter Limiter, reverse bool) net.PacketConn
)
type BandwidthStrategy interface {
@@ -25,7 +25,7 @@ type BandwidthStrategy interface {
}
type BandwidthLimiterStrategy interface {
getLimiter(ctx context.Context, metadata *adapter.InboundContext) (*rate.Limiter, CloseHandlerFunc, error)
getLimiter(ctx context.Context, metadata *adapter.InboundContext) (Limiter, CloseHandlerFunc, error)
}
type DefaultWrapStrategy struct {
@@ -55,21 +55,25 @@ func (s *DefaultWrapStrategy) wrapPacketConn(ctx context.Context, conn net.Packe
}
type GlobalBandwidthStrategy struct {
limiter *rate.Limiter
limiter Limiter
}
func NewGlobalBandwidthStrategy(speed uint64) *GlobalBandwidthStrategy {
return &GlobalBandwidthStrategy{
limiter: createSpeedLimiter(speed),
func NewGlobalBandwidthStrategy(speed uint64, flowKeys []string) (*GlobalBandwidthStrategy, error) {
limiter, err := createSpeedLimiter(speed, flowKeys)
if err != nil {
return nil, err
}
return &GlobalBandwidthStrategy{
limiter: limiter,
}, nil
}
func (s *GlobalBandwidthStrategy) getLimiter(ctx context.Context, metadata *adapter.InboundContext) (*rate.Limiter, CloseHandlerFunc, error) {
func (s *GlobalBandwidthStrategy) getLimiter(ctx context.Context, metadata *adapter.InboundContext) (Limiter, CloseHandlerFunc, error) {
return s.limiter, func() {}, nil
}
type idBandwidthLimiter struct {
limiter *rate.Limiter
limiter Limiter
handles uint32
}
@@ -77,18 +81,20 @@ type ConnectionBandwidthStrategy struct {
limiters map[string]*idBandwidthLimiter
connIDGetter ConnIDGetter
speed uint64
flowKeys []string
mtx sync.Mutex
}
func NewConnectionBandwidthStrategy(connIDGetter ConnIDGetter, speed uint64) *ConnectionBandwidthStrategy {
func NewConnectionBandwidthStrategy(connIDGetter ConnIDGetter, speed uint64, flowKeys []string) *ConnectionBandwidthStrategy {
return &ConnectionBandwidthStrategy{
limiters: make(map[string]*idBandwidthLimiter),
connIDGetter: connIDGetter,
speed: speed,
flowKeys: flowKeys,
}
}
func (s *ConnectionBandwidthStrategy) getLimiter(ctx context.Context, metadata *adapter.InboundContext) (*rate.Limiter, CloseHandlerFunc, error) {
func (s *ConnectionBandwidthStrategy) getLimiter(ctx context.Context, metadata *adapter.InboundContext) (Limiter, CloseHandlerFunc, error) {
s.mtx.Lock()
defer s.mtx.Unlock()
id, ok := s.connIDGetter(ctx, metadata)
@@ -97,8 +103,12 @@ func (s *ConnectionBandwidthStrategy) getLimiter(ctx context.Context, metadata *
}
limiter, ok := s.limiters[id]
if !ok {
newLimiter, err := createSpeedLimiter(s.speed, s.flowKeys)
if err != nil {
return nil, nil, err
}
limiter = &idBandwidthLimiter{
limiter: createSpeedLimiter(s.speed),
limiter: newLimiter,
}
s.limiters[id] = limiter
}
@@ -173,14 +183,37 @@ func (s *ManagerBandwidthStrategy) UpdateStrategies(strategies map[string]Bandwi
s.strategies = strategies
}
func CreateStrategy(strategy string, mode string, connectionType string, speed uint64) (BandwidthStrategy, error) {
type BypassBandwidthStrategy struct{}
func NewBypassBandwidthStrategy() *BypassBandwidthStrategy {
return &BypassBandwidthStrategy{}
}
func (s *BypassBandwidthStrategy) wrapConn(ctx context.Context, conn net.Conn, metadata *adapter.InboundContext, reverse bool) (net.Conn, error) {
return conn, nil
}
func (s *BypassBandwidthStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) {
return conn, nil
}
func CreateStrategy(strategy string, mode string, connectionType string, speed uint64, flowKeys []string) (BandwidthStrategy, error) {
var limiterStrategy BandwidthLimiterStrategy
switch strategy {
case "global":
limiterStrategy = NewGlobalBandwidthStrategy(speed)
var err error
limiterStrategy, err = NewGlobalBandwidthStrategy(speed, flowKeys)
if err != nil {
return nil, err
}
case "connection":
var connIDGetter ConnIDGetter
switch connectionType {
case "hwid":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := ctx.Value("hwid").(string)
return id, ok
}
case "mux":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := log.MuxIDFromContext(ctx)
@@ -189,19 +222,24 @@ func CreateStrategy(strategy string, mode string, connectionType string, speed u
}
return strconv.FormatUint(uint64(id.ID), 10), ok
}
case "hwid":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := ctx.Value("hwid").(string)
return id, ok
}
case "ip":
case "source_ip":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
return metadata.Source.IPAddr().String(), true
}
case "default", "":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := log.IDFromContext(ctx)
if !ok {
return "", ok
}
return strconv.FormatUint(uint64(id.ID), 10), ok
}
default:
return nil, E.New("connection type not found: ", connectionType)
}
limiterStrategy = NewConnectionBandwidthStrategy(connIDGetter, speed)
limiterStrategy = NewConnectionBandwidthStrategy(connIDGetter, speed, flowKeys)
case "bypass":
return NewBypassBandwidthStrategy(), nil
default:
return nil, E.New("strategy not found: ", strategy)
}
@@ -216,51 +254,55 @@ func CreateStrategy(strategy string, mode string, connectionType string, speed u
case "upload":
connWrapper = connWithUploadBandwidthWrapper
packetConnWrapper = packetConnWithUploadBandwidthWrapper
case "duplex":
connWrapper = connWithDuplexBandwidthWrapper
packetConnWrapper = packetConnWithDuplexBandwidthWrapper
case "bidirectional":
connWrapper = connWithBidirectionalBandwidthWrapper
packetConnWrapper = packetConnWithBidirectionalBandwidthWrapper
default:
return nil, E.New("mode not found: ", mode)
}
return NewDefaultWrapStrategy(limiterStrategy, connWrapper, packetConnWrapper), nil
}
func createSpeedLimiter(speed uint64) *rate.Limiter {
return rate.NewLimiter(rate.Limit(float64(speed)), 65536)
}
func connWithDownloadBandwidthWrapper(ctx context.Context, conn net.Conn, limiter *rate.Limiter, reverse bool) net.Conn {
if reverse {
return NewConnWithUploadBandwidthLimiter(ctx, conn, limiter)
func createSpeedLimiter(speed uint64, flowKeys []string) (Limiter, error) {
var limiter Limiter = rate.NewLimiter(rate.Limit(float64(speed)), 65536)
for i := len(flowKeys) - 1; i >= 0; i-- {
getter, err := flowKeysConnIDGetter(flowKeys[i])
if err != nil {
return nil, err
}
limiter = NewFlowKeysLimiter(getter, limiter)
}
return NewConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
return limiter, nil
}
func connWithUploadBandwidthWrapper(ctx context.Context, conn net.Conn, limiter *rate.Limiter, reverse bool) net.Conn {
if reverse {
return NewConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
func flowKeysConnIDGetter(name string) (ConnIDGetter, error) {
switch name {
case "user":
return func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
return metadata.User, true
}, nil
case "destination":
return func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
return metadata.Destination.String(), true
}, nil
case "source_ip":
return func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
return metadata.Source.IPAddr().String(), true
}, nil
case "hwid":
return func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := ctx.Value("hwid").(string)
return id, ok
}, nil
case "mux":
return func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := log.MuxIDFromContext(ctx)
if !ok {
return "", ok
}
return strconv.FormatUint(uint64(id.ID), 10), ok
}, nil
default:
return nil, E.New("flow key not found: ", name)
}
return NewConnWithUploadBandwidthLimiter(ctx, conn, limiter)
}
func connWithDuplexBandwidthWrapper(ctx context.Context, conn net.Conn, limiter *rate.Limiter, reverse bool) net.Conn {
return NewConnWithUploadBandwidthLimiter(ctx, NewConnWithDownloadBandwidthLimiter(ctx, conn, limiter), limiter)
}
func packetConnWithDownloadBandwidthWrapper(ctx context.Context, conn net.PacketConn, limiter *rate.Limiter, reverse bool) net.PacketConn {
if reverse {
return NewPacketConnWithUploadBandwidthLimiter(ctx, conn, limiter)
}
return NewPacketConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
}
func packetConnWithUploadBandwidthWrapper(ctx context.Context, conn net.PacketConn, limiter *rate.Limiter, reverse bool) net.PacketConn {
if reverse {
return NewPacketConnWithDownloadBandwidthLimiter(ctx, conn, limiter)
}
return NewPacketConnWithUploadBandwidthLimiter(ctx, conn, limiter)
}
func packetConnWithDuplexBandwidthWrapper(ctx context.Context, conn net.PacketConn, limiter *rate.Limiter, reverse bool) net.PacketConn {
return NewPacketConnWithUploadBandwidthLimiter(ctx, NewPacketConnWithDownloadBandwidthLimiter(ctx, conn, limiter), limiter)
}

View File

@@ -138,6 +138,7 @@ func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata
limiterOnClose, lockCtx, err := h.strategy.request(ctx, &metadata)
if err != nil {
h.logger.ErrorContext(ctx, err)
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
conn = newConnWithCloseHandlerFunc(conn, limiterOnClose)
@@ -154,6 +155,7 @@ func (h *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
limiterOnClose, lockCtx, err := h.strategy.request(ctx, &metadata)
if err != nil {
h.logger.ErrorContext(ctx, err)
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
conn = bufio.NewPacketConn(newPacketConnWithCloseHandlerFunc(bufio.NewNetPacketConn(conn), limiterOnClose))

View File

@@ -87,11 +87,26 @@ func (s *ManagerConnectionStrategy) UpdateStrategies(strategies map[string]Conne
s.strategies = strategies
}
type BypassConnectionStrategy struct{}
func NewBypassConnectionStrategy() *BypassConnectionStrategy {
return &BypassConnectionStrategy{}
}
func (s *BypassConnectionStrategy) request(ctx context.Context, metadata *adapter.InboundContext) (CloseHandlerFunc, context.Context, error) {
return func() {}, nil, nil
}
func CreateStrategy(strategy string, connectionType string, lockIDGetter LockIDGetter) (ConnectionStrategy, error) {
switch strategy {
case "connection":
var connIDGetter ConnIDGetter
switch connectionType {
case "hwid":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := ctx.Value("hwid").(string)
return id, ok
}
case "mux":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := log.MuxIDFromContext(ctx)
@@ -100,19 +115,24 @@ func CreateStrategy(strategy string, connectionType string, lockIDGetter LockIDG
}
return strconv.FormatUint(uint64(id.ID), 10), ok
}
case "hwid":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := ctx.Value("hwid").(string)
return id, ok
}
case "ip":
case "source_ip":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
return metadata.Source.IPAddr().String(), true
}
case "default", "":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := log.IDFromContext(ctx)
if !ok {
return "", ok
}
return strconv.FormatUint(uint64(id.ID), 10), ok
}
default:
return nil, E.New("connection type not found: ", connectionType)
}
return NewDefaultConnectionStrategy(connIDGetter, lockIDGetter), nil
case "bypass":
return NewBypassConnectionStrategy(), nil
default:
return nil, E.New("strategy not found: ", strategy)
}

View File

@@ -0,0 +1,140 @@
package rate
import (
"context"
"net"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/outbound"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
)
func RegisterOutbound(registry *outbound.Registry) {
outbound.Register[option.RateLimiterOutboundOptions](registry, C.TypeRateLimiter, NewOutbound)
}
type Outbound struct {
outbound.Adapter
ctx context.Context
outbound adapter.OutboundManager
connection adapter.ConnectionManager
logger logger.ContextLogger
strategy RateStrategy
outboundTag string
detour adapter.Outbound
router *route.Router
}
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.RateLimiterOutboundOptions) (adapter.Outbound, error) {
if options.Strategy == "" {
return nil, E.New("missing strategy")
}
if options.Route.Final == "" {
return nil, E.New("missing final outbound")
}
var strategy RateStrategy
var err error
switch options.Strategy {
case "users":
usersStrategies := make(map[string]RateStrategy, len(options.Users))
for _, user := range options.Users {
userStrategy, err := CreateStrategy(user.Strategy, user.ConnectionType, int(user.Count), user.Interval.Build())
if err != nil {
return nil, err
}
usersStrategies[user.Name] = userStrategy
}
strategy = NewUsersRateStrategy(usersStrategies)
case "manager":
strategy = NewManagerRateStrategy()
default:
strategy, err = CreateStrategy(options.Strategy, options.ConnectionType, int(options.Count), options.Interval.Build())
if err != nil {
return nil, err
}
}
logFactory := service.FromContext[log.Factory](ctx)
r := route.NewRouter(ctx, logFactory, options.Route, option.DNSOptions{})
err = r.Initialize(options.Route.Rules, options.Route.RuleSet)
if err != nil {
return nil, err
}
outbound := &Outbound{
Adapter: outbound.NewAdapter(C.TypeRateLimiter, tag, nil, []string{}),
ctx: ctx,
outbound: service.FromContext[adapter.OutboundManager](ctx),
connection: service.FromContext[adapter.ConnectionManager](ctx),
logger: logger,
outboundTag: options.Route.Final,
strategy: strategy,
router: r,
}
return outbound, nil
}
func (h *Outbound) Network() []string {
return []string{N.NetworkTCP, N.NetworkUDP}
}
func (h *Outbound) Start() error {
detour, loaded := h.outbound.Outbound(h.outboundTag)
if !loaded {
return E.New("outbound not found: ", h.outboundTag)
}
h.detour = detour
for _, stage := range []adapter.StartStage{adapter.StartStateStart, adapter.StartStatePostStart, adapter.StartStateStarted} {
err := h.router.Start(stage)
if err != nil {
return err
}
}
return nil
}
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if err := h.strategy.request(ctx, adapter.ContextFrom(ctx)); err != nil {
return nil, err
}
return h.detour.DialContext(ctx, network, destination)
}
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
if err := h.strategy.request(ctx, adapter.ContextFrom(ctx)); err != nil {
return nil, err
}
return h.detour.ListenPacket(ctx, destination)
}
func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
if err := h.strategy.request(ctx, &metadata); err != nil {
h.logger.ErrorContext(ctx, err)
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
metadata.Inbound = h.Tag()
metadata.InboundType = h.Type()
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}
func (h *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
if err := h.strategy.request(ctx, &metadata); err != nil {
h.logger.ErrorContext(ctx, err)
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
metadata.Inbound = h.Tag()
metadata.InboundType = h.Type()
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
}
func (h *Outbound) GetStrategy() RateStrategy {
return h.strategy
}

View File

@@ -0,0 +1,196 @@
package rate
import (
"context"
"strconv"
"strings"
"sync"
"time"
"github.com/AliRizaAynaci/gorl/v2"
"github.com/AliRizaAynaci/gorl/v2/core"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
E "github.com/sagernet/sing/common/exceptions"
"github.com/shtorm-7/go-cache/v2"
)
type (
ConnIDGetter = func(context.Context, *adapter.InboundContext) (string, bool)
RateGetter = func(id string) error
RateStrategy interface {
request(ctx context.Context, metadata *adapter.InboundContext) error
}
)
type DefaultRateStrategy struct {
limiter core.Limiter
queue chan struct{}
}
func NewDefaultRateStrategy(strategy string, count int, interval time.Duration) (*DefaultRateStrategy, error) {
limiter, err := gorl.New(core.Config{
Strategy: core.StrategyType(strings.ReplaceAll(strategy, "-", "_")),
Limit: count,
Window: interval,
})
if err != nil {
return nil, err
}
return &DefaultRateStrategy{limiter: limiter, queue: make(chan struct{}, 1)}, nil
}
func (s *DefaultRateStrategy) request(ctx context.Context, metadata *adapter.InboundContext) error {
select {
case s.queue <- struct{}{}:
case <-ctx.Done():
return ctx.Err()
}
defer func() {
<-s.queue
}()
r, err := s.limiter.Allow(ctx, metadata.Destination.String())
if err != nil {
return err
}
if !r.Allowed {
select {
case <-time.After(r.RetryAfter):
_, err = s.limiter.Allow(ctx, metadata.Destination.String())
return err
case <-ctx.Done():
return ctx.Err()
}
}
return nil
}
func (s *DefaultRateStrategy) close() error {
return s.limiter.Close()
}
type ConnectionRateStrategy struct {
connIDGetter ConnIDGetter
createStrategy func() (*DefaultRateStrategy, error)
limiters *cache.Cache[string, *DefaultRateStrategy]
mtx sync.Mutex
}
func NewConnectionRateStrategy(connIDGetter ConnIDGetter, strategy string, count int, interval time.Duration) (*ConnectionRateStrategy, error) {
limiters := cache.New[string, *DefaultRateStrategy](interval, time.Second)
limiters.OnEvicted(func(s string, strategy *DefaultRateStrategy) {
strategy.close()
})
return &ConnectionRateStrategy{
connIDGetter: connIDGetter,
createStrategy: func() (*DefaultRateStrategy, error) {
return NewDefaultRateStrategy(strategy, count, interval)
},
limiters: limiters,
}, nil
}
func (s *ConnectionRateStrategy) request(ctx context.Context, metadata *adapter.InboundContext) error {
id, ok := s.connIDGetter(ctx, metadata)
if !ok {
return E.New("id not found")
}
s.mtx.Lock()
strategy, ok := s.limiters.Get(id)
if !ok {
newStrategy, err := s.createStrategy()
if err != nil {
return err
}
s.limiters.SetDefault(id, newStrategy)
strategy = newStrategy
} else {
s.limiters.UpdateExpirationDefault(id)
}
s.mtx.Unlock()
return strategy.request(ctx, metadata)
}
type UsersRateStrategy struct {
strategies map[string]RateStrategy
mtx sync.Mutex
}
func NewUsersRateStrategy(strategies map[string]RateStrategy) *UsersRateStrategy {
return &UsersRateStrategy{
strategies: strategies,
}
}
func (s *UsersRateStrategy) request(ctx context.Context, metadata *adapter.InboundContext) error {
s.mtx.Lock()
defer s.mtx.Unlock()
var user string
if metadata != nil {
user = metadata.User
}
strategy, ok := s.strategies[user]
if ok {
return strategy.request(ctx, metadata)
}
return E.New("user strategy not found: ", user)
}
type ManagerRateStrategy struct {
*UsersRateStrategy
}
func NewManagerRateStrategy() *ManagerRateStrategy {
return &ManagerRateStrategy{
UsersRateStrategy: NewUsersRateStrategy(map[string]RateStrategy{}),
}
}
func (s *ManagerRateStrategy) UpdateStrategies(strategies map[string]RateStrategy) {
s.mtx.Lock()
defer s.mtx.Unlock()
s.strategies = strategies
}
type BypassRateStrategy struct{}
func NewBypassRateStrategy() *BypassRateStrategy {
return &BypassRateStrategy{}
}
func (s *BypassRateStrategy) request(ctx context.Context, metadata *adapter.InboundContext) error {
return nil
}
func CreateStrategy(strategy string, connectionType string, count int, interval time.Duration) (RateStrategy, error) {
if strategy == "bypass" {
return NewBypassRateStrategy(), nil
}
var connIDGetter ConnIDGetter
switch connectionType {
case "hwid":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := ctx.Value("hwid").(string)
return id, ok
}
case "mux":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
id, ok := log.MuxIDFromContext(ctx)
if !ok {
return "", ok
}
return strconv.FormatUint(uint64(id.ID), 10), ok
}
case "source_ip":
connIDGetter = func(ctx context.Context, metadata *adapter.InboundContext) (string, bool) {
return metadata.Source.IPAddr().String(), true
}
case "default", "":
return NewDefaultRateStrategy(strategy, count, interval)
default:
return nil, E.New("connection type not found: ", connectionType)
}
return NewConnectionRateStrategy(connIDGetter, strategy, count, interval)
}

View File

@@ -0,0 +1,146 @@
package traffic
import (
"context"
"net"
)
type connWithTrafficLimiter struct {
net.Conn
ctx context.Context
limiter TrafficLimiter
}
func newConnWithDownloadTrafficLimiter(ctx context.Context, conn net.Conn, limiter TrafficLimiter) net.Conn {
return &connWithTrafficLimiter{Conn: conn, ctx: ctx, limiter: limiter}
}
func newConnWithUploadTrafficLimiter(ctx context.Context, conn net.Conn, limiter TrafficLimiter) net.Conn {
return &connWithUploadTrafficLimiter{Conn: conn, ctx: ctx, limiter: limiter}
}
func (conn *connWithTrafficLimiter) Write(p []byte) (int, error) {
err := conn.limiter.Can(uint64(len(p)))
if err != nil {
return 0, err
}
n, err := conn.Conn.Write(p)
if err != nil {
return 0, err
}
err = conn.limiter.Add(uint64(n))
if err != nil {
return 0, err
}
return n, nil
}
type connWithUploadTrafficLimiter struct {
net.Conn
ctx context.Context
limiter TrafficLimiter
}
func (conn *connWithUploadTrafficLimiter) Read(p []byte) (int, error) {
err := conn.limiter.Can(1)
if err != nil {
return 0, err
}
n, err := conn.Conn.Read(p)
if err != nil {
return 0, err
}
err = conn.limiter.Add(uint64(n))
if err != nil {
return 0, err
}
return n, nil
}
type packetConnWithTrafficLimiter struct {
net.PacketConn
ctx context.Context
limiter TrafficLimiter
}
func newPacketConnWithDownloadTrafficLimiter(ctx context.Context, conn net.PacketConn, limiter TrafficLimiter) net.PacketConn {
return &packetConnWithTrafficLimiter{PacketConn: conn, ctx: ctx, limiter: limiter}
}
func newPacketConnWithUploadTrafficLimiter(ctx context.Context, conn net.PacketConn, limiter TrafficLimiter) net.PacketConn {
return &packetConnWithUploadTrafficLimiter{PacketConn: conn, ctx: ctx, limiter: limiter}
}
func (conn *packetConnWithTrafficLimiter) WriteTo(p []byte, addr net.Addr) (int, error) {
err := conn.limiter.Can(uint64(len(p)))
if err != nil {
return 0, err
}
n, err := conn.PacketConn.WriteTo(p, addr)
if err != nil {
return 0, err
}
err = conn.limiter.Add(uint64(n))
if err != nil {
return 0, err
}
return n, nil
}
type packetConnWithUploadTrafficLimiter struct {
net.PacketConn
ctx context.Context
limiter TrafficLimiter
}
func (conn *packetConnWithUploadTrafficLimiter) ReadFrom(p []byte) (int, net.Addr, error) {
err := conn.limiter.Can(1)
if err != nil {
return 0, nil, err
}
n, addr, err := conn.PacketConn.ReadFrom(p)
if err != nil {
return n, nil, err
}
err = conn.limiter.Add(uint64(n))
if err != nil {
return 0, nil, err
}
return n, addr, nil
}
func connWithDownloadTrafficWrapper(ctx context.Context, conn net.Conn, limiter TrafficLimiter, reverse bool) net.Conn {
if reverse {
return newConnWithUploadTrafficLimiter(ctx, conn, limiter)
}
return newConnWithDownloadTrafficLimiter(ctx, conn, limiter)
}
func connWithUploadTrafficWrapper(ctx context.Context, conn net.Conn, limiter TrafficLimiter, reverse bool) net.Conn {
if reverse {
return newConnWithDownloadTrafficLimiter(ctx, conn, limiter)
}
return newConnWithUploadTrafficLimiter(ctx, conn, limiter)
}
func connWithBidirectionalTrafficWrapper(ctx context.Context, conn net.Conn, limiter TrafficLimiter, reverse bool) net.Conn {
return newConnWithUploadTrafficLimiter(ctx, newConnWithDownloadTrafficLimiter(ctx, conn, limiter), limiter)
}
func packetConnWithDownloadTrafficWrapper(ctx context.Context, conn net.PacketConn, limiter TrafficLimiter, reverse bool) net.PacketConn {
if reverse {
return newPacketConnWithUploadTrafficLimiter(ctx, conn, limiter)
}
return newPacketConnWithDownloadTrafficLimiter(ctx, conn, limiter)
}
func packetConnWithUploadTrafficWrapper(ctx context.Context, conn net.PacketConn, limiter TrafficLimiter, reverse bool) net.PacketConn {
if reverse {
return newPacketConnWithDownloadTrafficLimiter(ctx, conn, limiter)
}
return newPacketConnWithUploadTrafficLimiter(ctx, conn, limiter)
}
func packetConnWithBidirectionalTrafficWrapper(ctx context.Context, conn net.PacketConn, limiter TrafficLimiter, reverse bool) net.PacketConn {
return newPacketConnWithUploadTrafficLimiter(ctx, newPacketConnWithDownloadTrafficLimiter(ctx, conn, limiter), limiter)
}

View File

@@ -0,0 +1,6 @@
package traffic
type TrafficLimiter interface {
Can(n uint64) error
Add(n uint64) error
}

View File

@@ -0,0 +1,126 @@
package traffic
import (
"context"
"net"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/outbound"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
)
func RegisterOutbound(registry *outbound.Registry) {
outbound.Register[option.TrafficLimiterOutboundOptions](registry, C.TypeTrafficLimiter, NewOutbound)
}
type Outbound struct {
outbound.Adapter
ctx context.Context
outbound adapter.OutboundManager
connection adapter.ConnectionManager
logger logger.ContextLogger
strategy TrafficStrategy
outboundTag string
detour adapter.Outbound
router *route.Router
}
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrafficLimiterOutboundOptions) (adapter.Outbound, error) {
if options.Route.Final == "" {
return nil, E.New("missing final outbound")
}
strategy := NewManagerTrafficStrategy()
logFactory := service.FromContext[log.Factory](ctx)
r := route.NewRouter(ctx, logFactory, options.Route, option.DNSOptions{})
err := r.Initialize(options.Route.Rules, options.Route.RuleSet)
if err != nil {
return nil, err
}
outbound := &Outbound{
Adapter: outbound.NewAdapter(C.TypeTrafficLimiter, tag, nil, []string{}),
ctx: ctx,
outbound: service.FromContext[adapter.OutboundManager](ctx),
connection: service.FromContext[adapter.ConnectionManager](ctx),
logger: logger,
strategy: strategy,
outboundTag: options.Route.Final,
router: r,
}
return outbound, nil
}
func (h *Outbound) Network() []string {
return []string{N.NetworkTCP, N.NetworkUDP}
}
func (h *Outbound) Start() error {
detour, loaded := h.outbound.Outbound(h.outboundTag)
if !loaded {
return E.New("outbound not found: ", h.outboundTag)
}
h.detour = detour
for _, stage := range []adapter.StartStage{adapter.StartStateStart, adapter.StartStatePostStart, adapter.StartStateStarted} {
err := h.router.Start(stage)
if err != nil {
return err
}
}
return nil
}
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
conn, err := h.detour.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
return h.strategy.wrapConn(ctx, conn, adapter.ContextFrom(ctx), true)
}
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
conn, err := h.detour.ListenPacket(ctx, destination)
if err != nil {
return nil, err
}
return h.strategy.wrapPacketConn(ctx, conn, adapter.ContextFrom(ctx), true)
}
func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
conn, err := h.strategy.wrapConn(ctx, conn, &metadata, false)
if err != nil {
if err.Error() != "traffic limit exceeded" {
h.logger.ErrorContext(ctx, err)
}
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
metadata.Inbound = h.Tag()
metadata.InboundType = h.Type()
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}
func (h *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
packetConn, err := h.strategy.wrapPacketConn(ctx, bufio.NewNetPacketConn(conn), &metadata, false)
if err != nil {
if err.Error() != "traffic limit exceeded" {
h.logger.ErrorContext(ctx, err)
}
N.CloseOnHandshakeFailure(conn, onClose, err)
return
}
metadata.Inbound = h.Tag()
metadata.InboundType = h.Type()
h.router.RoutePacketConnectionEx(ctx, bufio.NewPacketConn(packetConn), metadata, onClose)
}
func (h *Outbound) GetStrategy() TrafficStrategy {
return h.strategy
}

View File

@@ -0,0 +1,164 @@
package traffic
import (
"context"
"net"
"sync"
"github.com/sagernet/sing-box/adapter"
E "github.com/sagernet/sing/common/exceptions"
)
type (
CloseHandlerFunc = func()
ConnWrapper = func(ctx context.Context, conn net.Conn, limiter TrafficLimiter, reverse bool) net.Conn
PacketConnWrapper = func(ctx context.Context, conn net.PacketConn, limiter TrafficLimiter, reverse bool) net.PacketConn
)
type TrafficStrategy interface {
wrapConn(ctx context.Context, conn net.Conn, metadata *adapter.InboundContext, reverse bool) (net.Conn, error)
wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error)
}
type TrafficLimiterStrategy interface {
getLimiter(ctx context.Context, metadata *adapter.InboundContext) (TrafficLimiter, error)
}
type DefaultWrapStrategy struct {
limiterStrategy TrafficLimiterStrategy
connWrapper ConnWrapper
packetConnWrapper PacketConnWrapper
}
func NewDefaultWrapStrategy(limiterStrategy TrafficLimiterStrategy, connWrapper ConnWrapper, packetConnWrapper PacketConnWrapper) *DefaultWrapStrategy {
return &DefaultWrapStrategy{limiterStrategy, connWrapper, packetConnWrapper}
}
func (s *DefaultWrapStrategy) wrapConn(ctx context.Context, conn net.Conn, metadata *adapter.InboundContext, reverse bool) (net.Conn, error) {
limiter, err := s.limiterStrategy.getLimiter(ctx, metadata)
if err != nil {
return conn, err
}
err = limiter.Can(1)
if err != nil {
return conn, err
}
return s.connWrapper(ctx, conn, limiter, reverse), nil
}
func (s *DefaultWrapStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) {
limiter, err := s.limiterStrategy.getLimiter(ctx, metadata)
if err != nil {
return conn, err
}
err = limiter.Can(1)
if err != nil {
return conn, err
}
return s.packetConnWrapper(ctx, conn, limiter, reverse), nil
}
type GlobalTrafficStrategy struct {
limiter TrafficLimiter
}
func NewGlobalTrafficStrategy(limiter TrafficLimiter) *GlobalTrafficStrategy {
return &GlobalTrafficStrategy{
limiter: limiter,
}
}
func (s *GlobalTrafficStrategy) getLimiter(ctx context.Context, metadata *adapter.InboundContext) (TrafficLimiter, error) {
return s.limiter, nil
}
type ManagerTrafficStrategy struct {
strategies map[string]TrafficStrategy
mtx sync.Mutex
}
func NewManagerTrafficStrategy() *ManagerTrafficStrategy {
return &ManagerTrafficStrategy{}
}
func (s *ManagerTrafficStrategy) wrapConn(ctx context.Context, conn net.Conn, metadata *adapter.InboundContext, reverse bool) (net.Conn, error) {
strategy, err := s.getStrategy(ctx, metadata)
if err != nil {
return nil, err
}
return strategy.wrapConn(ctx, conn, metadata, reverse)
}
func (s *ManagerTrafficStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) {
strategy, err := s.getStrategy(ctx, metadata)
if err != nil {
return nil, err
}
return strategy.wrapPacketConn(ctx, conn, metadata, reverse)
}
func (s *ManagerTrafficStrategy) getStrategy(ctx context.Context, metadata *adapter.InboundContext) (TrafficStrategy, error) {
s.mtx.Lock()
defer s.mtx.Unlock()
var user string
if metadata != nil {
user = metadata.User
}
strategy, ok := s.strategies[user]
if ok {
return strategy, nil
}
return nil, E.New("user strategy not found: ", user)
}
func (s *ManagerTrafficStrategy) UpdateStrategies(strategies map[string]TrafficStrategy) {
s.mtx.Lock()
defer s.mtx.Unlock()
s.strategies = strategies
}
type BypassTrafficStrategy struct{}
func NewBypassTrafficStrategy() *BypassTrafficStrategy {
return &BypassTrafficStrategy{}
}
func (s *BypassTrafficStrategy) wrapConn(ctx context.Context, conn net.Conn, metadata *adapter.InboundContext, reverse bool) (net.Conn, error) {
return conn, nil
}
func (s *BypassTrafficStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) {
return conn, nil
}
func CreateStrategy(limiter TrafficLimiter, strategy string, mode string) (TrafficStrategy, error) {
switch strategy {
case "bypass":
return NewBypassTrafficStrategy(), nil
case "global", "":
var (
connWrapper ConnWrapper
packetConnWrapper PacketConnWrapper
)
switch mode {
case "download":
connWrapper = connWithDownloadTrafficWrapper
packetConnWrapper = packetConnWithDownloadTrafficWrapper
case "upload":
connWrapper = connWithUploadTrafficWrapper
packetConnWrapper = packetConnWithUploadTrafficWrapper
case "bidirectional":
connWrapper = connWithBidirectionalTrafficWrapper
packetConnWrapper = packetConnWithBidirectionalTrafficWrapper
default:
return nil, E.New("mode not found: ", mode)
}
return NewDefaultWrapStrategy(
NewGlobalTrafficStrategy(limiter),
connWrapper,
packetConnWrapper,
), nil
default:
return nil, E.New("strategy not found: ", strategy)
}
}