mirror of
https://github.com/wyx2685/V2bX.git
synced 2025-01-22 09:58:14 -05:00
981e59b836
Some checks are pending
Build and Release / build (386, freebsd) (push) Waiting to run
Build and Release / build (386, linux) (push) Waiting to run
Build and Release / build (386, windows) (push) Waiting to run
Build and Release / build (amd64, darwin) (push) Waiting to run
Build and Release / build (amd64, freebsd) (push) Waiting to run
Build and Release / build (amd64, linux) (push) Waiting to run
Build and Release / build (amd64, windows) (push) Waiting to run
Build and Release / build (arm, 5, linux) (push) Waiting to run
Build and Release / build (arm, 6, linux) (push) Waiting to run
Build and Release / build (arm, 7, freebsd) (push) Waiting to run
Build and Release / build (arm, 7, linux) (push) Waiting to run
Build and Release / build (arm64, android) (push) Waiting to run
Build and Release / build (arm64, darwin) (push) Waiting to run
Build and Release / build (arm64, freebsd) (push) Waiting to run
Build and Release / build (arm64, linux) (push) Waiting to run
Build and Release / build (mips, linux) (push) Waiting to run
Build and Release / build (mips64, linux) (push) Waiting to run
Build and Release / build (mips64le, linux) (push) Waiting to run
Build and Release / build (mipsle, linux) (push) Waiting to run
Build and Release / build (ppc64, linux) (push) Waiting to run
Build and Release / build (ppc64le, linux) (push) Waiting to run
Build and Release / build (riscv64, linux) (push) Waiting to run
Build and Release / build (s390x, linux) (push) Waiting to run
236 lines
6.0 KiB
Go
236 lines
6.0 KiB
Go
package limiter
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/InazumaV/V2bX/api/panel"
|
|
"github.com/InazumaV/V2bX/common/format"
|
|
"github.com/InazumaV/V2bX/conf"
|
|
"github.com/juju/ratelimit"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/xtls/xray-core/common/task"
|
|
)
|
|
|
|
var limitLock sync.RWMutex
|
|
var limiter map[string]*Limiter
|
|
|
|
func Init() {
|
|
limiter = map[string]*Limiter{}
|
|
c := task.Periodic{
|
|
Interval: time.Minute * 3,
|
|
Execute: ClearOnlineIP,
|
|
}
|
|
go func() {
|
|
log.WithField("Type", "Limiter").
|
|
Debug("ClearOnlineIP started")
|
|
time.Sleep(time.Minute * 3)
|
|
_ = c.Start()
|
|
}()
|
|
}
|
|
|
|
type Limiter struct {
|
|
DomainRules []*regexp.Regexp
|
|
ProtocolRules []string
|
|
SpeedLimit int
|
|
UserOnlineIP *sync.Map // Key: Name, value: {Key: Ip, value: Uid}
|
|
OldUserOnline *sync.Map // Key: Ip, value: Uid
|
|
UUIDtoUID map[string]int // Key: UUID, value: Uid
|
|
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
|
|
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
|
|
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
|
|
AliveList map[int]int // Key: Uid, value: alive_ip
|
|
}
|
|
|
|
type UserLimitInfo struct {
|
|
UID int
|
|
SpeedLimit int
|
|
DeviceLimit int
|
|
DynamicSpeedLimit int
|
|
ExpireTime int64
|
|
OverLimit bool
|
|
}
|
|
|
|
func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo, aliveList map[int]int) *Limiter {
|
|
info := &Limiter{
|
|
SpeedLimit: l.SpeedLimit,
|
|
UserOnlineIP: new(sync.Map),
|
|
UserLimitInfo: new(sync.Map),
|
|
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
|
|
SpeedLimiter: new(sync.Map),
|
|
AliveList: aliveList,
|
|
OldUserOnline: new(sync.Map),
|
|
}
|
|
uuidmap := make(map[string]int)
|
|
for i := range users {
|
|
uuidmap[users[i].Uuid] = users[i].Id
|
|
userLimit := &UserLimitInfo{}
|
|
userLimit.UID = users[i].Id
|
|
if users[i].SpeedLimit != 0 {
|
|
userLimit.SpeedLimit = users[i].SpeedLimit
|
|
}
|
|
if users[i].DeviceLimit != 0 {
|
|
userLimit.DeviceLimit = users[i].DeviceLimit
|
|
}
|
|
userLimit.OverLimit = false
|
|
info.UserLimitInfo.Store(format.UserTag(tag, users[i].Uuid), userLimit)
|
|
}
|
|
info.UUIDtoUID = uuidmap
|
|
limitLock.Lock()
|
|
limiter[tag] = info
|
|
limitLock.Unlock()
|
|
return info
|
|
}
|
|
|
|
func GetLimiter(tag string) (info *Limiter, err error) {
|
|
limitLock.RLock()
|
|
info, ok := limiter[tag]
|
|
limitLock.RUnlock()
|
|
if !ok {
|
|
return nil, errors.New("not found")
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func DeleteLimiter(tag string) {
|
|
limitLock.Lock()
|
|
delete(limiter, tag)
|
|
limitLock.Unlock()
|
|
}
|
|
|
|
func (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, deleted []panel.UserInfo) {
|
|
for i := range deleted {
|
|
l.UserLimitInfo.Delete(format.UserTag(tag, deleted[i].Uuid))
|
|
l.UserOnlineIP.Delete(format.UserTag(tag, deleted[i].Uuid))
|
|
delete(l.UUIDtoUID, deleted[i].Uuid)
|
|
delete(l.AliveList, deleted[i].Id)
|
|
}
|
|
for i := range added {
|
|
userLimit := &UserLimitInfo{
|
|
UID: added[i].Id,
|
|
}
|
|
if added[i].SpeedLimit != 0 {
|
|
userLimit.SpeedLimit = added[i].SpeedLimit
|
|
userLimit.ExpireTime = 0
|
|
}
|
|
if added[i].DeviceLimit != 0 {
|
|
userLimit.DeviceLimit = added[i].DeviceLimit
|
|
}
|
|
userLimit.OverLimit = false
|
|
l.UserLimitInfo.Store(format.UserTag(tag, added[i].Uuid), userLimit)
|
|
l.UUIDtoUID[added[i].Uuid] = added[i].Id
|
|
}
|
|
}
|
|
|
|
func (l *Limiter) UpdateDynamicSpeedLimit(tag, uuid string, limit int, expire time.Time) error {
|
|
if v, ok := l.UserLimitInfo.Load(format.UserTag(tag, uuid)); ok {
|
|
info := v.(*UserLimitInfo)
|
|
info.DynamicSpeedLimit = limit
|
|
info.ExpireTime = expire.Unix()
|
|
} else {
|
|
return errors.New("not found")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool) (Bucket *ratelimit.Bucket, Reject bool) {
|
|
// check if ipv4 mapped ipv6
|
|
ip = strings.TrimPrefix(ip, "::ffff:")
|
|
|
|
// ip and conn limiter
|
|
if l.ConnLimiter.AddConnCount(taguuid, ip, isTcp) {
|
|
return nil, true
|
|
}
|
|
// check and gen speed limit Bucket
|
|
nodeLimit := l.SpeedLimit
|
|
userLimit := 0
|
|
deviceLimit := 0
|
|
var uid int
|
|
if v, ok := l.UserLimitInfo.Load(taguuid); ok {
|
|
u := v.(*UserLimitInfo)
|
|
deviceLimit = u.DeviceLimit
|
|
uid = u.UID
|
|
if u.ExpireTime < time.Now().Unix() && u.ExpireTime != 0 {
|
|
if u.SpeedLimit != 0 {
|
|
userLimit = u.SpeedLimit
|
|
u.DynamicSpeedLimit = 0
|
|
u.ExpireTime = 0
|
|
} else {
|
|
l.UserLimitInfo.Delete(taguuid)
|
|
}
|
|
} else {
|
|
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
|
|
}
|
|
}
|
|
if noSSUDP {
|
|
// Store online user for device limit
|
|
ipMap := new(sync.Map)
|
|
ipMap.Store(ip, uid)
|
|
aliveIp := l.AliveList[uid]
|
|
// If any device is online
|
|
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
|
|
ipMap := v.(*sync.Map)
|
|
// If this is a new ip
|
|
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
|
|
if deviceLimit > 0 {
|
|
if deviceLimit <= aliveIp {
|
|
ipMap.Delete(ip)
|
|
return nil, true
|
|
}
|
|
}
|
|
}
|
|
} else if v, ok := l.OldUserOnline.Load(ip); ok{
|
|
if v.(int) == uid {
|
|
l.OldUserOnline.Delete(ip)
|
|
}
|
|
} else {
|
|
if deviceLimit > 0 {
|
|
if deviceLimit <= aliveIp {
|
|
l.UserOnlineIP.Delete(taguuid)
|
|
return nil, true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
limit := int64(determineSpeedLimit(nodeLimit, userLimit)) * 1000000 / 8 // If you need the Speed limit
|
|
if limit > 0 {
|
|
Bucket = ratelimit.NewBucketWithQuantum(time.Second, limit, limit) // Byte/s
|
|
if v, ok := l.SpeedLimiter.LoadOrStore(taguuid, Bucket); ok {
|
|
return v.(*ratelimit.Bucket), false
|
|
} else {
|
|
l.SpeedLimiter.Store(taguuid, Bucket)
|
|
return Bucket, false
|
|
}
|
|
} else {
|
|
return nil, false
|
|
}
|
|
}
|
|
|
|
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
|
|
var onlineUser []panel.OnlineUser
|
|
l.UserOnlineIP.Range(func(key, value interface{}) bool {
|
|
taguuid := key.(string)
|
|
ipMap := value.(*sync.Map)
|
|
ipMap.Range(func(key, value interface{}) bool {
|
|
uid := value.(int)
|
|
ip := key.(string)
|
|
l.OldUserOnline.Store(ip, uid)
|
|
onlineUser = append(onlineUser, panel.OnlineUser{UID: uid, IP: ip})
|
|
return true
|
|
})
|
|
l.UserOnlineIP.Delete(taguuid) // Reset online device
|
|
return true
|
|
})
|
|
|
|
return &onlineUser, nil
|
|
}
|
|
|
|
type UserIpList struct {
|
|
Uid int `json:"Uid"`
|
|
IpList []string `json:"Ips"`
|
|
}
|