mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-25 21:51:47 +03:00
Add new admin panel, failover, dns fallback, providers, limiters. Update XHTTP
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/protocol/limiter/bandwidth"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
@@ -15,14 +17,21 @@ type ManagedBandwidthStrategy interface {
|
||||
}
|
||||
|
||||
type BandwidthLimiterManager struct {
|
||||
ctx context.Context
|
||||
nodeManager CM.NodeManager
|
||||
logger log.ContextLogger
|
||||
|
||||
managers map[string]*BandwidthLimiterStrategyManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewBandwidthLimiterManager() *BandwidthLimiterManager {
|
||||
func NewBandwidthLimiterManager(ctx context.Context, nodeManager CM.NodeManager, logger log.ContextLogger) *BandwidthLimiterManager {
|
||||
return &BandwidthLimiterManager{
|
||||
managers: make(map[string]*BandwidthLimiterStrategyManager),
|
||||
ctx: ctx,
|
||||
nodeManager: nodeManager,
|
||||
logger: logger,
|
||||
managers: make(map[string]*BandwidthLimiterStrategyManager),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +47,7 @@ func (m *BandwidthLimiterManager) AddBandwidthLimiterStrategyManager(outbound ad
|
||||
return E.New("strategy for outbound ", outbound.Tag(), " is not manager")
|
||||
}
|
||||
m.managers[outbound.Tag()] = &BandwidthLimiterStrategyManager{
|
||||
manager: m,
|
||||
strategy: strategy,
|
||||
strategiesMap: make(map[string]bandwidth.BandwidthStrategy),
|
||||
}
|
||||
@@ -55,13 +65,14 @@ func (m *BandwidthLimiterManager) GetBandwidthLimiterStrategyManagerTags() []str
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.managers))
|
||||
for tag, _ := range m.managers {
|
||||
for tag := range m.managers {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type BandwidthLimiterStrategyManager struct {
|
||||
manager *BandwidthLimiterManager
|
||||
strategy ManagedBandwidthStrategy
|
||||
strategiesMap map[string]bandwidth.BandwidthStrategy
|
||||
|
||||
@@ -75,8 +86,9 @@ func (i *BandwidthLimiterStrategyManager) postUpdate() {
|
||||
func (i *BandwidthLimiterStrategyManager) UpdateBandwidthLimiter(limiter CM.BandwidthLimiter) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
strategy, err := bandwidth.CreateStrategy(limiter.Strategy, limiter.Mode, limiter.ConnectionType, limiter.RawSpeed)
|
||||
strategy, err := bandwidth.CreateStrategy(limiter.Strategy, limiter.Mode, limiter.ConnectionType, limiter.RawSpeed, limiter.FlowKeys)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
return
|
||||
}
|
||||
i.strategiesMap[limiter.Username] = strategy
|
||||
@@ -89,9 +101,10 @@ func (i *BandwidthLimiterStrategyManager) UpdateBandwidthLimiters(limiters []CM.
|
||||
clear(i.strategiesMap)
|
||||
newStrategiesMap := make(map[string]bandwidth.BandwidthStrategy)
|
||||
for _, limiter := range limiters {
|
||||
strategy, err := bandwidth.CreateStrategy(limiter.Strategy, limiter.Mode, limiter.ConnectionType, limiter.RawSpeed)
|
||||
strategy, err := bandwidth.CreateStrategy(limiter.Strategy, limiter.Mode, limiter.ConnectionType, limiter.RawSpeed, limiter.FlowKeys)
|
||||
if err != nil {
|
||||
return
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
continue
|
||||
}
|
||||
newStrategiesMap[limiter.Username] = strategy
|
||||
}
|
||||
|
||||
@@ -18,18 +18,21 @@ type ManagedConnectionStrategy interface {
|
||||
}
|
||||
|
||||
type ConnectionLimiterManager struct {
|
||||
ctx context.Context
|
||||
nodeManager CM.NodeManager
|
||||
managers map[string]*ConnectionLimiterStrategyManager
|
||||
logger log.Logger
|
||||
logger log.ContextLogger
|
||||
|
||||
managers map[string]*ConnectionLimiterStrategyManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnectionLimiterManager(nodeManager CM.NodeManager, logger log.Logger) *ConnectionLimiterManager {
|
||||
func NewConnectionLimiterManager(ctx context.Context, nodeManager CM.NodeManager, logger log.ContextLogger) *ConnectionLimiterManager {
|
||||
return &ConnectionLimiterManager{
|
||||
ctx: ctx,
|
||||
nodeManager: nodeManager,
|
||||
managers: make(map[string]*ConnectionLimiterStrategyManager),
|
||||
logger: logger,
|
||||
managers: make(map[string]*ConnectionLimiterStrategyManager),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +48,9 @@ func (m *ConnectionLimiterManager) AddConnectionLimiterStrategyManager(outbound
|
||||
return E.New("strategy ", strategy, " is not manager")
|
||||
}
|
||||
m.managers[outbound.Tag()] = &ConnectionLimiterStrategyManager{
|
||||
manager: m,
|
||||
strategy: strategy,
|
||||
strategiesMap: make(map[string]connection.ConnectionStrategy),
|
||||
manager: m,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -63,17 +66,17 @@ func (m *ConnectionLimiterManager) GetConnectionLimiterStrategyManagerTags() []s
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.managers))
|
||||
for tag, _ := range m.managers {
|
||||
for tag := range m.managers {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type ConnectionLimiterStrategyManager struct {
|
||||
manager *ConnectionLimiterManager
|
||||
strategy ManagedConnectionStrategy
|
||||
strategiesMap map[string]connection.ConnectionStrategy
|
||||
tag string
|
||||
manager *ConnectionLimiterManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
@@ -87,10 +90,12 @@ func (i *ConnectionLimiterStrategyManager) UpdateConnectionLimiter(limiter CM.Co
|
||||
defer i.mtx.Unlock()
|
||||
lock, err := i.createLock(limiter)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
return
|
||||
}
|
||||
strategy, err := connection.CreateStrategy(limiter.Strategy, limiter.ConnectionType, lock)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
return
|
||||
}
|
||||
i.strategiesMap[limiter.Username] = strategy
|
||||
@@ -105,11 +110,13 @@ func (i *ConnectionLimiterStrategyManager) UpdateConnectionLimiters(limiters []C
|
||||
for _, limiter := range limiters {
|
||||
lock, err := i.createLock(limiter)
|
||||
if err != nil {
|
||||
return
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
continue
|
||||
}
|
||||
strategy, err := connection.CreateStrategy(limiter.Strategy, limiter.ConnectionType, lock)
|
||||
if err != nil {
|
||||
return
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
continue
|
||||
}
|
||||
newStrategiesMap[limiter.Username] = strategy
|
||||
}
|
||||
@@ -128,7 +135,7 @@ func (i *ConnectionLimiterStrategyManager) createLock(limiter CM.ConnectionLimit
|
||||
switch limiter.LockType {
|
||||
case "manager":
|
||||
return i.newManagerLock(limiter.ID), nil
|
||||
case "":
|
||||
case "default", "":
|
||||
return connection.NewDefaultLock(limiter.Count), nil
|
||||
default:
|
||||
return nil, E.New("unknown lock type \"", limiter.LockType, "\"")
|
||||
|
||||
129
service/node/limiter/rate.go
Normal file
129
service/node/limiter/rate.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/protocol/limiter/rate"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type ManagedRateStrategy interface {
|
||||
UpdateStrategies(strategies map[string]rate.RateStrategy)
|
||||
}
|
||||
|
||||
type RateLimiterManager struct {
|
||||
ctx context.Context
|
||||
nodeManager CM.NodeManager
|
||||
logger log.ContextLogger
|
||||
|
||||
managers map[string]*RateLimiterStrategyManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewRateLimiterManager(ctx context.Context, nodeManager CM.NodeManager, logger log.ContextLogger) *RateLimiterManager {
|
||||
return &RateLimiterManager{
|
||||
ctx: ctx,
|
||||
nodeManager: nodeManager,
|
||||
logger: logger,
|
||||
managers: make(map[string]*RateLimiterStrategyManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *RateLimiterManager) AddRateLimiterStrategyManager(outbound adapter.Outbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
limiter, ok := outbound.(*rate.Outbound)
|
||||
if !ok {
|
||||
return E.New("invalid rate limiter: ", outbound.Tag())
|
||||
}
|
||||
strategy, ok := limiter.GetStrategy().(ManagedRateStrategy)
|
||||
if !ok {
|
||||
return E.New("strategy for outbound ", outbound.Tag(), " is not manager")
|
||||
}
|
||||
m.managers[outbound.Tag()] = &RateLimiterStrategyManager{
|
||||
manager: m,
|
||||
strategy: strategy,
|
||||
strategiesMap: make(map[string]rate.RateStrategy),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *RateLimiterManager) GetRateLimiterStrategyManager(tag string) (constant.RateLimiterStrategyManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
manager, ok := m.managers[tag]
|
||||
return manager, ok
|
||||
}
|
||||
|
||||
func (m *RateLimiterManager) GetRateLimiterStrategyManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.managers))
|
||||
for tag := range m.managers {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type RateLimiterStrategyManager struct {
|
||||
manager *RateLimiterManager
|
||||
strategy ManagedRateStrategy
|
||||
strategiesMap map[string]rate.RateStrategy
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *RateLimiterStrategyManager) postUpdate() {
|
||||
i.strategy.UpdateStrategies(i.strategiesMap)
|
||||
}
|
||||
|
||||
func (i *RateLimiterStrategyManager) createStrategy(limiter CM.RateLimiter) (rate.RateStrategy, error) {
|
||||
interval, err := time.ParseDuration(limiter.Interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rate.CreateStrategy(limiter.Strategy, limiter.ConnectionType, int(limiter.Count), interval)
|
||||
}
|
||||
|
||||
func (i *RateLimiterStrategyManager) UpdateRateLimiter(limiter CM.RateLimiter) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
strategy, err := i.createStrategy(limiter)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
return
|
||||
}
|
||||
i.strategiesMap[limiter.Username] = strategy
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *RateLimiterStrategyManager) UpdateRateLimiters(limiters []CM.RateLimiter) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.strategiesMap)
|
||||
newStrategiesMap := make(map[string]rate.RateStrategy)
|
||||
for _, limiter := range limiters {
|
||||
strategy, err := i.createStrategy(limiter)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
continue
|
||||
}
|
||||
newStrategiesMap[limiter.Username] = strategy
|
||||
}
|
||||
i.strategiesMap = newStrategiesMap
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *RateLimiterStrategyManager) DeleteRateLimiter(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.strategiesMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
216
service/node/limiter/traffic.go
Normal file
216
service/node/limiter/traffic.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/protocol/limiter/traffic"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type ManagedTrafficStrategy interface {
|
||||
UpdateStrategies(strategies map[string]traffic.TrafficStrategy)
|
||||
}
|
||||
|
||||
type TrafficLimiterManager struct {
|
||||
ctx context.Context
|
||||
nodeManager CM.NodeManager
|
||||
logger log.ContextLogger
|
||||
|
||||
managers map[string]*TrafficLimiterStrategyManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewTrafficLimiterManager(ctx context.Context, nodeManager CM.NodeManager, logger log.ContextLogger) *TrafficLimiterManager {
|
||||
manager := &TrafficLimiterManager{
|
||||
ctx: ctx,
|
||||
nodeManager: nodeManager,
|
||||
logger: logger,
|
||||
managers: make(map[string]*TrafficLimiterStrategyManager),
|
||||
}
|
||||
go func() {
|
||||
timer := time.NewTimer(time.Second * 5)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case <-timer.C:
|
||||
for _, strategyManager := range manager.managers {
|
||||
strategyManager.mtx.Lock()
|
||||
for _, limiter := range strategyManager.limiters {
|
||||
err := limiter.UpdateRemainingTraffic()
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, err)
|
||||
}
|
||||
}
|
||||
strategyManager.mtx.Unlock()
|
||||
}
|
||||
timer.Reset(time.Second * 5)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return manager
|
||||
}
|
||||
|
||||
func (m *TrafficLimiterManager) AddTrafficLimiterStrategyManager(outbound adapter.Outbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
limiter, ok := outbound.(*traffic.Outbound)
|
||||
if !ok {
|
||||
return E.New("invalid traffic limiter: ", outbound.Tag())
|
||||
}
|
||||
strategy, ok := limiter.GetStrategy().(ManagedTrafficStrategy)
|
||||
if !ok {
|
||||
return E.New("strategy ", outbound.Tag(), " is not manager")
|
||||
}
|
||||
m.managers[outbound.Tag()] = &TrafficLimiterStrategyManager{
|
||||
manager: m,
|
||||
strategy: strategy,
|
||||
strategiesMap: make(map[string]traffic.TrafficStrategy),
|
||||
limiters: make(map[string]*TrafficLimiter),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *TrafficLimiterManager) GetTrafficLimiterStrategyManager(tag string) (constant.TrafficLimiterStrategyManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
manager, ok := m.managers[tag]
|
||||
return manager, ok
|
||||
}
|
||||
|
||||
func (m *TrafficLimiterManager) GetTrafficLimiterStrategyManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.managers))
|
||||
for tag := range m.managers {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type TrafficLimiterStrategyManager struct {
|
||||
manager *TrafficLimiterManager
|
||||
strategy ManagedTrafficStrategy
|
||||
strategiesMap map[string]traffic.TrafficStrategy
|
||||
limiters map[string]*TrafficLimiter
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *TrafficLimiterStrategyManager) postUpdate() {
|
||||
i.strategy.UpdateStrategies(i.strategiesMap)
|
||||
}
|
||||
|
||||
func (i *TrafficLimiterStrategyManager) UpdateTrafficLimiter(limiter CM.TrafficLimiter) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
trafficLimiter := NewTrafficLimiter(i.manager.nodeManager, limiter)
|
||||
strategy, err := traffic.CreateStrategy(trafficLimiter, limiter.Strategy, limiter.Mode)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
return
|
||||
}
|
||||
i.limiters[limiter.Username] = trafficLimiter
|
||||
i.strategiesMap[limiter.Username] = strategy
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *TrafficLimiterStrategyManager) UpdateTrafficLimiters(limiters []CM.TrafficLimiter) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.strategiesMap)
|
||||
newStrategiesMap := make(map[string]traffic.TrafficStrategy)
|
||||
for _, limiter := range limiters {
|
||||
trafficLimiter := NewTrafficLimiter(i.manager.nodeManager, limiter)
|
||||
strategy, err := traffic.CreateStrategy(trafficLimiter, limiter.Strategy, limiter.Mode)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
continue
|
||||
}
|
||||
i.limiters[limiter.Username] = trafficLimiter
|
||||
newStrategiesMap[limiter.Username] = strategy
|
||||
}
|
||||
i.strategiesMap = newStrategiesMap
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *TrafficLimiterStrategyManager) DeleteTrafficLimiter(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.strategiesMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
type TrafficLimiter struct {
|
||||
manager CM.NodeManager
|
||||
limiter CM.TrafficLimiter
|
||||
new uint64
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewTrafficLimiter(manager CM.NodeManager, limiter CM.TrafficLimiter) *TrafficLimiter {
|
||||
return &TrafficLimiter{manager: manager, limiter: limiter}
|
||||
}
|
||||
|
||||
func (l *TrafficLimiter) Can(n uint64) error {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
if l.limiter.RawUsed == l.limiter.RawQuota {
|
||||
return E.New("traffic limit exceeded")
|
||||
}
|
||||
if l.limiter.RawUsed+n > l.limiter.RawQuota {
|
||||
l.new += l.limiter.RawQuota - l.limiter.RawUsed
|
||||
l.limiter.RawUsed = l.limiter.RawQuota
|
||||
return E.New("traffic limit exceeded")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *TrafficLimiter) Add(n uint64) error {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
if l.limiter.RawUsed == l.limiter.RawQuota {
|
||||
return E.New("traffic limit exceeded")
|
||||
}
|
||||
if l.limiter.RawUsed+n > l.limiter.RawQuota {
|
||||
l.new += l.limiter.RawQuota - l.limiter.RawUsed
|
||||
l.limiter.RawUsed = l.limiter.RawQuota
|
||||
return E.New("traffic limit exceeded")
|
||||
}
|
||||
l.limiter.RawUsed += n
|
||||
l.new += n
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *TrafficLimiter) UpdateRemainingTraffic() error {
|
||||
l.mtx.Lock()
|
||||
if l.new == 0 {
|
||||
l.mtx.Unlock()
|
||||
return nil
|
||||
}
|
||||
new := l.new
|
||||
l.new = 0
|
||||
l.mtx.Unlock()
|
||||
newUsed, err := l.manager.AddTrafficUsage(l.limiter.ID, new)
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
if err == nil {
|
||||
l.limiter.RawUsed = newUsed
|
||||
} else {
|
||||
l.new += new
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user