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"` }