Add admin panel, manager, node_manager, bandwidth limiter, connection limiter, bonding, failover, vless encryption, mkcp transport

This commit is contained in:
Sergei Maklagin
2026-02-26 22:44:31 +03:00
parent 287fe834db
commit c0aa3480c5
115 changed files with 12582 additions and 301 deletions

View File

@@ -0,0 +1,18 @@
package constant
import (
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/service/manager/constant"
)
type BandwidthLimiterManager interface {
AddBandwidthLimiterStrategyManager(outbound adapter.Outbound) error
GetBandwidthLimiterStrategyManager(tag string) (BandwidthLimiterStrategyManager, bool)
GetBandwidthLimiterStrategyManagerTags() []string
}
type BandwidthLimiterStrategyManager interface {
UpdateBandwidthLimiter(limiter C.BandwidthLimiter)
UpdateBandwidthLimiters(limiter []C.BandwidthLimiter)
DeleteBandwidthLimiter(username string)
}

View File

@@ -0,0 +1,18 @@
package constant
import (
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/service/manager/constant"
)
type ConnectionLimiterManager interface {
AddConnectionLimiterStrategyManager(outbound adapter.Outbound) error
GetConnectionLimiterStrategyManager(tag string) (ConnectionLimiterStrategyManager, bool)
GetConnectionLimiterStrategyManagerTags() []string
}
type ConnectionLimiterStrategyManager interface {
UpdateConnectionLimiter(limiter C.ConnectionLimiter)
UpdateConnectionLimiters(limiter []C.ConnectionLimiter)
DeleteConnectionLimiter(username string)
}

View File

@@ -0,0 +1,18 @@
package constant
import (
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/service/manager/constant"
)
type InboundManager interface {
AddUserManager(inbound adapter.Inbound) error
GetUserManager(tag string) (UserManager, bool)
GetUserManagerTags() []string
}
type UserManager interface {
UpdateUser(user C.User)
UpdateUsers(users []C.User)
DeleteUser(username string)
}

View File

@@ -0,0 +1,88 @@
package inbound
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/hysteria"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
)
type HysteriaManager struct {
access sync.Mutex
inbounds map[string]*HysteriaUserManager
}
func NewHysteriaManager() *HysteriaManager {
return &HysteriaManager{
inbounds: make(map[string]*HysteriaUserManager),
}
}
func (m *HysteriaManager) AddUserManager(inbound adapter.Inbound) error {
m.access.Lock()
defer m.access.Unlock()
m.inbounds[inbound.Tag()] = &HysteriaUserManager{
inbound: inbound.(*hysteria.Inbound),
usersMap: make(map[string]option.HysteriaUser),
}
return nil
}
func (m *HysteriaManager) GetUserManager(tag string) (constant.UserManager, bool) {
m.access.Lock()
defer m.access.Unlock()
inbound, ok := m.inbounds[tag]
return inbound, ok
}
func (m *HysteriaManager) GetUserManagerTags() []string {
m.access.Lock()
defer m.access.Unlock()
tags := make([]string, 0, len(m.inbounds))
for tag, _ := range m.inbounds {
tags = append(tags, tag)
}
return tags
}
type HysteriaUserManager struct {
inbound *hysteria.Inbound
usersMap map[string]option.HysteriaUser
mtx sync.Mutex
}
func (i *HysteriaUserManager) postUpdate() {
users := make([]option.HysteriaUser, 0, len(i.usersMap))
for _, user := range i.usersMap {
users = append(users, user)
}
i.inbound.UpdateUsers(users)
}
func (i *HysteriaUserManager) UpdateUser(user CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
i.usersMap[user.Username] = option.HysteriaUser{Name: user.Username, AuthString: user.Password}
i.postUpdate()
}
func (i *HysteriaUserManager) UpdateUsers(users []CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.usersMap)
for _, user := range users {
i.usersMap[user.Username] = option.HysteriaUser{Name: user.Username, AuthString: user.Password}
}
i.postUpdate()
}
func (i *HysteriaUserManager) DeleteUser(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.usersMap, username)
i.postUpdate()
}

View File

@@ -0,0 +1,88 @@
package inbound
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/hysteria2"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
)
type Hysteria2Manager struct {
access sync.Mutex
inbounds map[string]*Hysteria2UserManager
}
func NewHysteria2Manager() *Hysteria2Manager {
return &Hysteria2Manager{
inbounds: make(map[string]*Hysteria2UserManager),
}
}
func (m *Hysteria2Manager) AddUserManager(inbound adapter.Inbound) error {
m.access.Lock()
defer m.access.Unlock()
m.inbounds[inbound.Tag()] = &Hysteria2UserManager{
inbound: inbound.(*hysteria2.Inbound),
usersMap: make(map[string]option.Hysteria2User),
}
return nil
}
func (m *Hysteria2Manager) GetUserManager(tag string) (constant.UserManager, bool) {
m.access.Lock()
defer m.access.Unlock()
inbound, ok := m.inbounds[tag]
return inbound, ok
}
func (m *Hysteria2Manager) GetUserManagerTags() []string {
m.access.Lock()
defer m.access.Unlock()
tags := make([]string, 0, len(m.inbounds))
for tag, _ := range m.inbounds {
tags = append(tags, tag)
}
return tags
}
type Hysteria2UserManager struct {
inbound *hysteria2.Inbound
usersMap map[string]option.Hysteria2User
mtx sync.Mutex
}
func (i *Hysteria2UserManager) postUpdate() {
users := make([]option.Hysteria2User, 0, len(i.usersMap))
for _, user := range i.usersMap {
users = append(users, user)
}
i.inbound.UpdateUsers(users)
}
func (i *Hysteria2UserManager) UpdateUser(user CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
i.usersMap[user.Username] = option.Hysteria2User{Name: user.Username, Password: user.Password}
i.postUpdate()
}
func (i *Hysteria2UserManager) UpdateUsers(users []CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.usersMap)
for _, user := range users {
i.usersMap[user.Username] = option.Hysteria2User{Name: user.Username, Password: user.Password}
}
i.postUpdate()
}
func (i *Hysteria2UserManager) DeleteUser(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.usersMap, username)
i.postUpdate()
}

View File

@@ -0,0 +1,88 @@
package inbound
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/trojan"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
)
type TrojanManager struct {
access sync.Mutex
inbounds map[string]*TrojanUserManager
}
func NewTrojanManager() *TrojanManager {
return &TrojanManager{
inbounds: make(map[string]*TrojanUserManager),
}
}
func (m *TrojanManager) AddUserManager(inbound adapter.Inbound) error {
m.access.Lock()
defer m.access.Unlock()
m.inbounds[inbound.Tag()] = &TrojanUserManager{
inbound: inbound.(*trojan.Inbound),
usersMap: make(map[string]option.TrojanUser),
}
return nil
}
func (m *TrojanManager) GetUserManager(tag string) (constant.UserManager, bool) {
m.access.Lock()
defer m.access.Unlock()
inbound, ok := m.inbounds[tag]
return inbound, ok
}
func (m *TrojanManager) GetUserManagerTags() []string {
m.access.Lock()
defer m.access.Unlock()
tags := make([]string, 0, len(m.inbounds))
for tag, _ := range m.inbounds {
tags = append(tags, tag)
}
return tags
}
type TrojanUserManager struct {
inbound *trojan.Inbound
usersMap map[string]option.TrojanUser
mtx sync.Mutex
}
func (i *TrojanUserManager) postUpdate() {
users := make([]option.TrojanUser, 0, len(i.usersMap))
for _, user := range i.usersMap {
users = append(users, user)
}
i.inbound.UpdateUsers(users)
}
func (i *TrojanUserManager) UpdateUser(user CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
i.usersMap[user.Username] = option.TrojanUser{Name: user.Username, Password: user.Password}
i.postUpdate()
}
func (i *TrojanUserManager) UpdateUsers(users []CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.usersMap)
for _, user := range users {
i.usersMap[user.Username] = option.TrojanUser{Name: user.Username, Password: user.Password}
}
i.postUpdate()
}
func (i *TrojanUserManager) DeleteUser(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.usersMap, username)
i.postUpdate()
}

View File

@@ -0,0 +1,88 @@
package inbound
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/tuic"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
)
type TUICManager struct {
access sync.Mutex
inbounds map[string]*TUICUserManager
}
func NewTUICManager() *TUICManager {
return &TUICManager{
inbounds: make(map[string]*TUICUserManager),
}
}
func (m *TUICManager) AddUserManager(inbound adapter.Inbound) error {
m.access.Lock()
defer m.access.Unlock()
m.inbounds[inbound.Tag()] = &TUICUserManager{
inbound: inbound.(*tuic.Inbound),
usersMap: make(map[string]option.TUICUser),
}
return nil
}
func (m *TUICManager) GetUserManager(tag string) (constant.UserManager, bool) {
m.access.Lock()
defer m.access.Unlock()
inbound, ok := m.inbounds[tag]
return inbound, ok
}
func (m *TUICManager) GetUserManagerTags() []string {
m.access.Lock()
defer m.access.Unlock()
tags := make([]string, 0, len(m.inbounds))
for tag, _ := range m.inbounds {
tags = append(tags, tag)
}
return tags
}
type TUICUserManager struct {
inbound *tuic.Inbound
usersMap map[string]option.TUICUser
mtx sync.Mutex
}
func (i *TUICUserManager) postUpdate() {
users := make([]option.TUICUser, 0, len(i.usersMap))
for _, user := range i.usersMap {
users = append(users, user)
}
i.inbound.UpdateUsers(users)
}
func (i *TUICUserManager) UpdateUser(user CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
i.usersMap[user.Username] = option.TUICUser{Name: user.Username, UUID: user.UUID, Password: user.Password}
i.postUpdate()
}
func (i *TUICUserManager) UpdateUsers(users []CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.usersMap)
for _, user := range users {
i.usersMap[user.Username] = option.TUICUser{Name: user.Username, UUID: user.UUID, Password: user.Password}
}
i.postUpdate()
}
func (i *TUICUserManager) DeleteUser(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.usersMap, username)
i.postUpdate()
}

View File

@@ -0,0 +1,88 @@
package inbound
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/vless"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
)
type VLESSManager struct {
access sync.Mutex
inbounds map[string]*VLESSUserManager
}
func NewVLESSManager() *VLESSManager {
return &VLESSManager{
inbounds: make(map[string]*VLESSUserManager),
}
}
func (m *VLESSManager) AddUserManager(inbound adapter.Inbound) error {
m.access.Lock()
defer m.access.Unlock()
m.inbounds[inbound.Tag()] = &VLESSUserManager{
inbound: inbound.(*vless.Inbound),
usersMap: make(map[string]option.VLESSUser),
}
return nil
}
func (m *VLESSManager) GetUserManager(tag string) (constant.UserManager, bool) {
m.access.Lock()
defer m.access.Unlock()
inbound, ok := m.inbounds[tag]
return inbound, ok
}
func (m *VLESSManager) GetUserManagerTags() []string {
m.access.Lock()
defer m.access.Unlock()
tags := make([]string, 0, len(m.inbounds))
for tag, _ := range m.inbounds {
tags = append(tags, tag)
}
return tags
}
type VLESSUserManager struct {
inbound *vless.Inbound
usersMap map[string]option.VLESSUser
mtx sync.Mutex
}
func (i *VLESSUserManager) postUpdate() {
users := make([]option.VLESSUser, 0, len(i.usersMap))
for _, user := range i.usersMap {
users = append(users, user)
}
i.inbound.UpdateUsers(users)
}
func (i *VLESSUserManager) UpdateUser(user CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
i.usersMap[user.Username] = option.VLESSUser{Name: user.Username, UUID: user.UUID, Flow: user.Flow}
i.postUpdate()
}
func (i *VLESSUserManager) UpdateUsers(users []CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.usersMap)
for _, user := range users {
i.usersMap[user.Username] = option.VLESSUser{Name: user.Username, UUID: user.UUID, Flow: user.Flow}
}
i.postUpdate()
}
func (i *VLESSUserManager) DeleteUser(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.usersMap, username)
i.postUpdate()
}

View File

@@ -0,0 +1,88 @@
package inbound
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/vmess"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
)
type VMessManager struct {
inbounds map[string]*VMessUserManager
mtx sync.Mutex
}
func NewVMessManager() *VMessManager {
return &VMessManager{
inbounds: make(map[string]*VMessUserManager),
}
}
func (m *VMessManager) AddUserManager(inbound adapter.Inbound) error {
m.mtx.Lock()
defer m.mtx.Unlock()
m.inbounds[inbound.Tag()] = &VMessUserManager{
inbound: inbound.(*vmess.Inbound),
usersMap: make(map[string]option.VMessUser),
}
return nil
}
func (m *VMessManager) GetUserManager(tag string) (constant.UserManager, bool) {
m.mtx.Lock()
defer m.mtx.Unlock()
inbound, ok := m.inbounds[tag]
return inbound, ok
}
func (m *VMessManager) GetUserManagerTags() []string {
m.mtx.Lock()
defer m.mtx.Unlock()
tags := make([]string, 0, len(m.inbounds))
for tag, _ := range m.inbounds {
tags = append(tags, tag)
}
return tags
}
type VMessUserManager struct {
inbound *vmess.Inbound
usersMap map[string]option.VMessUser
mtx sync.Mutex
}
func (i *VMessUserManager) postUpdate() {
users := make([]option.VMessUser, 0, len(i.usersMap))
for _, user := range i.usersMap {
users = append(users, user)
}
i.inbound.UpdateUsers(users)
}
func (i *VMessUserManager) UpdateUser(user CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
i.usersMap[user.Username] = option.VMessUser{Name: user.Username, UUID: user.UUID, AlterId: user.AlterID}
i.postUpdate()
}
func (i *VMessUserManager) UpdateUsers(users []CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.usersMap)
for _, user := range users {
i.usersMap[user.Username] = option.VMessUser{Name: user.Username, UUID: user.UUID, AlterId: user.AlterID}
}
i.postUpdate()
}
func (i *VMessUserManager) DeleteUser(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.usersMap, username)
i.postUpdate()
}

View File

@@ -0,0 +1,107 @@
package limiter
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"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"
E "github.com/sagernet/sing/common/exceptions"
)
type ManagedBandwidthStrategy interface {
UpdateStrategies(strategies map[string]bandwidth.BandwidthStrategy)
}
type BandwidthLimiterManager struct {
managers map[string]*BandwidthLimiterStrategyManager
mtx sync.Mutex
}
func NewBandwidthLimiterManager() *BandwidthLimiterManager {
return &BandwidthLimiterManager{
managers: make(map[string]*BandwidthLimiterStrategyManager),
}
}
func (m *BandwidthLimiterManager) AddBandwidthLimiterStrategyManager(outbound adapter.Outbound) error {
m.mtx.Lock()
defer m.mtx.Unlock()
limiter, ok := outbound.(*bandwidth.Outbound)
if !ok {
return E.New("invalid bandwidth limiter: ", outbound.Tag())
}
strategy, ok := limiter.GetStrategy().(ManagedBandwidthStrategy)
if !ok {
return E.New("strategy for outbound ", outbound.Tag(), " is not manager")
}
m.managers[outbound.Tag()] = &BandwidthLimiterStrategyManager{
strategy: strategy,
strategiesMap: make(map[string]bandwidth.BandwidthStrategy),
}
return nil
}
func (m *BandwidthLimiterManager) GetBandwidthLimiterStrategyManager(tag string) (constant.BandwidthLimiterStrategyManager, bool) {
m.mtx.Lock()
defer m.mtx.Unlock()
manager, ok := m.managers[tag]
return manager, ok
}
func (m *BandwidthLimiterManager) GetBandwidthLimiterStrategyManagerTags() []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 BandwidthLimiterStrategyManager struct {
strategy ManagedBandwidthStrategy
strategiesMap map[string]bandwidth.BandwidthStrategy
mtx sync.Mutex
}
func (i *BandwidthLimiterStrategyManager) postUpdate() {
i.strategy.UpdateStrategies(i.strategiesMap)
}
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)
if err != nil {
return
}
i.strategiesMap[limiter.Username] = strategy
i.postUpdate()
}
func (i *BandwidthLimiterStrategyManager) UpdateBandwidthLimiters(limiters []CM.BandwidthLimiter) {
i.mtx.Lock()
defer i.mtx.Unlock()
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)
if err != nil {
return
}
newStrategiesMap[limiter.Username] = strategy
}
i.strategiesMap = newStrategiesMap
i.postUpdate()
}
func (i *BandwidthLimiterStrategyManager) DeleteBandwidthLimiter(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.strategiesMap, username)
i.postUpdate()
}

View File

@@ -0,0 +1,195 @@
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/connection"
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 ManagedConnectionStrategy interface {
UpdateStrategies(strategies map[string]connection.ConnectionStrategy)
}
type ConnectionLimiterManager struct {
nodeManager CM.NodeManager
managers map[string]*ConnectionLimiterStrategyManager
logger log.Logger
mtx sync.Mutex
}
func NewConnectionLimiterManager(nodeManager CM.NodeManager, logger log.Logger) *ConnectionLimiterManager {
return &ConnectionLimiterManager{
nodeManager: nodeManager,
managers: make(map[string]*ConnectionLimiterStrategyManager),
logger: logger,
}
}
func (m *ConnectionLimiterManager) AddConnectionLimiterStrategyManager(outbound adapter.Outbound) error {
m.mtx.Lock()
defer m.mtx.Unlock()
limiter, ok := outbound.(*connection.Outbound)
if !ok {
return E.New("invalid connection limiter: ", outbound.Tag())
}
strategy, ok := limiter.GetStrategy().(ManagedConnectionStrategy)
if !ok {
return E.New("strategy ", strategy, " is not manager")
}
m.managers[outbound.Tag()] = &ConnectionLimiterStrategyManager{
strategy: strategy,
strategiesMap: make(map[string]connection.ConnectionStrategy),
manager: m,
}
return nil
}
func (m *ConnectionLimiterManager) GetConnectionLimiterStrategyManager(tag string) (constant.ConnectionLimiterStrategyManager, bool) {
m.mtx.Lock()
defer m.mtx.Unlock()
manager, ok := m.managers[tag]
return manager, ok
}
func (m *ConnectionLimiterManager) GetConnectionLimiterStrategyManagerTags() []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 ConnectionLimiterStrategyManager struct {
strategy ManagedConnectionStrategy
strategiesMap map[string]connection.ConnectionStrategy
tag string
manager *ConnectionLimiterManager
mtx sync.Mutex
}
func (i *ConnectionLimiterStrategyManager) postUpdate() {
i.strategy.UpdateStrategies(i.strategiesMap)
}
func (i *ConnectionLimiterStrategyManager) UpdateConnectionLimiter(limiter CM.ConnectionLimiter) {
i.mtx.Lock()
defer i.mtx.Unlock()
lock, err := i.createLock(limiter)
if err != nil {
return
}
strategy, err := connection.CreateStrategy(limiter.Strategy, limiter.ConnectionType, lock)
if err != nil {
return
}
i.strategiesMap[limiter.Username] = strategy
i.postUpdate()
}
func (i *ConnectionLimiterStrategyManager) UpdateConnectionLimiters(limiters []CM.ConnectionLimiter) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.strategiesMap)
newStrategiesMap := make(map[string]connection.ConnectionStrategy)
for _, limiter := range limiters {
lock, err := i.createLock(limiter)
if err != nil {
return
}
strategy, err := connection.CreateStrategy(limiter.Strategy, limiter.ConnectionType, lock)
if err != nil {
return
}
newStrategiesMap[limiter.Username] = strategy
}
i.strategiesMap = newStrategiesMap
i.postUpdate()
}
func (i *ConnectionLimiterStrategyManager) DeleteConnectionLimiter(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.strategiesMap, username)
i.postUpdate()
}
func (i *ConnectionLimiterStrategyManager) createLock(limiter CM.ConnectionLimiter) (connection.LockIDGetter, error) {
switch limiter.LockType {
case "manager":
return i.newManagerLock(limiter.ID), nil
case "":
return connection.NewDefaultLock(limiter.Count), nil
default:
return nil, E.New("unknown lock type \"", limiter.LockType, "\"")
}
}
type ManagerLock struct {
handleId string
ctx context.Context
cancel context.CancelFunc
handles uint32
}
func (i *ConnectionLimiterStrategyManager) newManagerLock(limiterId int) connection.LockIDGetter {
conns := make(map[string]*ManagerLock)
mtx := sync.Mutex{}
return func(id string) (connection.CloseHandlerFunc, context.Context, error) {
mtx.Lock()
defer mtx.Unlock()
conn, ok := conns[id]
if !ok {
nodeManager := i.manager.nodeManager
handleId, err := nodeManager.AcquireLock(limiterId, id)
if err != nil {
return nil, nil, err
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * 5):
err := nodeManager.RefreshLock(limiterId, id, handleId)
if err != nil {
cancel()
return
}
}
}
}()
conn = &ManagerLock{
handleId: handleId,
ctx: ctx,
cancel: cancel,
}
conns[id] = conn
}
conn.handles++
var once sync.Once
return func() {
once.Do(func() {
mtx.Lock()
defer mtx.Unlock()
conn.handles--
if conn.handles == 0 {
conn.cancel()
i.manager.nodeManager.ReleaseLock(limiterId, id, conn.handleId)
delete(conns, id)
}
})
}, conn.ctx, nil
}
}

235
service/node/service.go Normal file
View File

@@ -0,0 +1,235 @@
package node
import (
"context"
"sync"
"github.com/sagernet/sing-box/adapter"
boxService "github.com/sagernet/sing-box/adapter/service"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
"github.com/sagernet/sing-box/service/node/inbound"
"github.com/sagernet/sing-box/service/node/limiter"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service"
)
func RegisterService(registry *boxService.Registry) {
boxService.Register[option.NodeServiceOptions](registry, C.TypeNode, NewService)
}
type Service struct {
boxService.Adapter
ctx context.Context
logger log.ContextLogger
inboundManagers map[string]constant.InboundManager
bandwidthManager constant.BandwidthLimiterManager
connectionManager constant.ConnectionLimiterManager
options option.NodeServiceOptions
mtx sync.Mutex
}
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.NodeServiceOptions) (adapter.Service, error) {
return &Service{
Adapter: boxService.NewAdapter(C.TypeManager, tag),
ctx: ctx,
logger: logger,
options: options,
}, nil
}
func (s *Service) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateStart {
return nil
}
boxManager := service.FromContext[adapter.ServiceManager](s.ctx)
serviceManager, ok := boxManager.Get(s.options.Manager)
if !ok {
return E.New("manager ", s.options.Manager, " not found")
}
nodeManager, ok := serviceManager.(CM.NodeManager)
if !ok {
return E.New("invalid ", s.options.Manager, " manager")
}
inboundManager := service.FromContext[adapter.InboundManager](s.ctx)
outboundManager := service.FromContext[adapter.OutboundManager](s.ctx)
s.inboundManagers = map[string]constant.InboundManager{
"hysteria": inbound.NewHysteriaManager(),
"hysteria2": inbound.NewHysteria2Manager(),
"trojan": inbound.NewTrojanManager(),
"tuic": inbound.NewTUICManager(),
"vless": inbound.NewVLESSManager(),
"vmess": inbound.NewVMessManager(),
}
s.connectionManager = limiter.NewConnectionLimiterManager(nodeManager, s.logger)
s.bandwidthManager = limiter.NewBandwidthLimiterManager()
for _, tag := range s.options.Inbounds {
inbound, ok := inboundManager.Get(tag)
if !ok {
return E.New("inbound ", tag, " not found")
}
inboundManager, ok := s.inboundManagers[inbound.Type()]
if !ok {
return E.New("inbound manager for ", tag, " not found")
}
err := inboundManager.AddUserManager(inbound)
if err != nil {
return err
}
}
for _, limiter := range s.options.ConnectionLimiters {
outbound, ok := outboundManager.Outbound(limiter)
if !ok {
return E.New("outbound ", limiter, " not found")
}
err := s.connectionManager.AddConnectionLimiterStrategyManager(outbound)
if err != nil {
return err
}
}
for _, limiter := range s.options.BandwidthLimiters {
outbound, ok := outboundManager.Outbound(limiter)
if !ok {
return E.New("outbound ", limiter, " not found")
}
err := s.bandwidthManager.AddBandwidthLimiterStrategyManager(outbound)
if err != nil {
return err
}
}
return nodeManager.AddNode(s.options.UUID, s)
}
func (s *Service) UpdateUser(user CM.User) {
s.mtx.Lock()
defer s.mtx.Unlock()
manager, ok := s.inboundManagers[user.Type]
if !ok {
return
}
userManager, ok := manager.GetUserManager(user.Inbound)
if !ok {
return
}
userManager.UpdateUser(user)
}
func (s *Service) UpdateUsers(users []CM.User) {
s.mtx.Lock()
defer s.mtx.Unlock()
typedUsers := make(map[string][]CM.User)
for _, user := range users {
u, ok := typedUsers[user.Type]
if !ok {
typedUsers[user.Type] = make([]CM.User, 0)
}
typedUsers[user.Type] = append(u, user)
}
for type_, users := range typedUsers {
manager, ok := s.inboundManagers[type_]
if !ok {
continue
}
for _, user := range users {
userManager, ok := manager.GetUserManager(user.Inbound)
if !ok {
continue
}
userManager.UpdateUsers(users)
}
}
}
func (s *Service) DeleteUser(user CM.User) {
s.mtx.Lock()
defer s.mtx.Unlock()
manager, ok := s.inboundManagers[user.Type]
if !ok {
return
}
userManager, ok := manager.GetUserManager(user.Inbound)
if !ok {
return
}
userManager.DeleteUser(user.Username)
}
func (s *Service) UpdateConnectionLimiter(limiter CM.ConnectionLimiter) {
s.mtx.Lock()
defer s.mtx.Unlock()
manager, ok := s.connectionManager.GetConnectionLimiterStrategyManager(limiter.Outbound)
if !ok {
return
}
manager.UpdateConnectionLimiter(limiter)
}
func (s *Service) UpdateConnectionLimiters(limiters []CM.ConnectionLimiter) {
s.mtx.Lock()
defer s.mtx.Unlock()
for _, limiter := range limiters {
manager, ok := s.connectionManager.GetConnectionLimiterStrategyManager(limiter.Outbound)
if !ok {
continue
}
manager.UpdateConnectionLimiters(limiters)
}
}
func (s *Service) DeleteConnectionLimiter(limiter CM.ConnectionLimiter) {
s.mtx.Lock()
defer s.mtx.Unlock()
manager, ok := s.connectionManager.GetConnectionLimiterStrategyManager(limiter.Outbound)
if !ok {
return
}
manager.DeleteConnectionLimiter(limiter.Username)
}
func (s *Service) UpdateBandwidthLimiter(limiter CM.BandwidthLimiter) {
s.mtx.Lock()
defer s.mtx.Unlock()
manager, ok := s.bandwidthManager.GetBandwidthLimiterStrategyManager(limiter.Outbound)
if !ok {
return
}
manager.UpdateBandwidthLimiter(limiter)
}
func (s *Service) UpdateBandwidthLimiters(limiters []CM.BandwidthLimiter) {
s.mtx.Lock()
defer s.mtx.Unlock()
for _, limiter := range limiters {
manager, ok := s.bandwidthManager.GetBandwidthLimiterStrategyManager(limiter.Outbound)
if !ok {
continue
}
manager.UpdateBandwidthLimiters(limiters)
}
}
func (s *Service) DeleteBandwidthLimiter(limiter CM.BandwidthLimiter) {
s.mtx.Lock()
defer s.mtx.Unlock()
manager, ok := s.bandwidthManager.GetBandwidthLimiterStrategyManager(limiter.Outbound)
if !ok {
return
}
manager.DeleteBandwidthLimiter(limiter.Username)
}
func (s *Service) IsLocal() bool {
return true
}
func (s *Service) IsOnline() bool {
return true
}
func (s *Service) Close() error {
return nil
}