2022-08-16 01:04:33 -04:00
|
|
|
package dispatcher
|
2022-06-01 13:35:41 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-08-16 01:04:33 -04:00
|
|
|
"github.com/Yuzuki616/V2bX/api/panel"
|
|
|
|
"github.com/juju/ratelimit"
|
|
|
|
"github.com/xtls/xray-core/common"
|
|
|
|
"github.com/xtls/xray-core/common/buf"
|
|
|
|
"io"
|
2022-06-01 13:35:41 -04:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type UserInfo struct {
|
|
|
|
UID int
|
|
|
|
SpeedLimit uint64
|
|
|
|
DeviceLimit int
|
|
|
|
}
|
|
|
|
|
|
|
|
type InboundInfo struct {
|
|
|
|
Tag string
|
|
|
|
NodeSpeedLimit uint64
|
2022-07-28 11:00:05 -04:00
|
|
|
UserInfo *sync.Map // Key: Uid value: UserInfo
|
2022-09-05 20:28:57 -04:00
|
|
|
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
|
2022-07-28 11:00:05 -04:00
|
|
|
UserOnlineIP *sync.Map // Key: Uid Value: *sync.Map: Key: IP, Value: bool
|
2022-06-01 13:35:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type Limiter struct {
|
|
|
|
InboundInfo *sync.Map // Key: Tag, Value: *InboundInfo
|
|
|
|
}
|
|
|
|
|
2022-08-16 01:04:33 -04:00
|
|
|
func NewLimiter() *Limiter {
|
2022-06-01 13:35:41 -04:00
|
|
|
return &Limiter{
|
|
|
|
InboundInfo: new(sync.Map),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-16 01:04:33 -04:00
|
|
|
func (l *Limiter) AddInboundLimiter(tag string, nodeInfo *panel.NodeInfo, userList []panel.UserInfo) error {
|
2022-06-01 13:35:41 -04:00
|
|
|
inboundInfo := &InboundInfo{
|
|
|
|
Tag: tag,
|
2022-06-04 00:05:46 -04:00
|
|
|
NodeSpeedLimit: nodeInfo.SpeedLimit,
|
2022-09-05 20:28:57 -04:00
|
|
|
SpeedLimiter: new(sync.Map),
|
2022-06-01 13:35:41 -04:00
|
|
|
UserOnlineIP: new(sync.Map),
|
|
|
|
}
|
|
|
|
userMap := new(sync.Map)
|
2022-08-12 06:02:06 -04:00
|
|
|
for i := range userList {
|
2022-06-13 02:47:01 -04:00
|
|
|
/*if (*userList)[i].SpeedLimit == 0 {
|
2022-06-04 00:05:46 -04:00
|
|
|
(*userList)[i].SpeedLimit = nodeInfo.SpeedLimit
|
|
|
|
}
|
|
|
|
if (*userList)[i].DeviceLimit == 0 {
|
|
|
|
(*userList)[i].DeviceLimit = nodeInfo.DeviceLimit
|
2022-06-13 02:47:01 -04:00
|
|
|
}*/
|
2022-08-12 06:02:06 -04:00
|
|
|
userMap.Store(fmt.Sprintf("%s|%s|%d", tag, (userList)[i].V2rayUser.Email, (userList)[i].UID),
|
|
|
|
UserInfo{
|
|
|
|
UID: (userList)[i].UID,
|
|
|
|
SpeedLimit: nodeInfo.SpeedLimit,
|
|
|
|
DeviceLimit: nodeInfo.DeviceLimit,
|
|
|
|
})
|
2022-06-01 13:35:41 -04:00
|
|
|
}
|
|
|
|
inboundInfo.UserInfo = userMap
|
|
|
|
l.InboundInfo.Store(tag, inboundInfo) // Replace the old inbound info
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-08-16 01:04:33 -04:00
|
|
|
func (l *Limiter) UpdateInboundLimiter(tag string, nodeInfo *panel.NodeInfo, updatedUserList []panel.UserInfo) error {
|
2022-06-01 13:35:41 -04:00
|
|
|
if value, ok := l.InboundInfo.Load(tag); ok {
|
|
|
|
inboundInfo := value.(*InboundInfo)
|
|
|
|
// Update User info
|
2022-08-12 06:02:06 -04:00
|
|
|
for i := range updatedUserList {
|
2022-07-28 11:00:05 -04:00
|
|
|
inboundInfo.UserInfo.Store(fmt.Sprintf("%s|%s|%d", tag,
|
2022-08-12 06:02:06 -04:00
|
|
|
(updatedUserList)[i].V2rayUser.Email, (updatedUserList)[i].UID), UserInfo{
|
|
|
|
UID: (updatedUserList)[i].UID,
|
2022-06-13 02:47:01 -04:00
|
|
|
SpeedLimit: nodeInfo.SpeedLimit,
|
|
|
|
DeviceLimit: nodeInfo.DeviceLimit,
|
2022-06-01 13:35:41 -04:00
|
|
|
})
|
2022-09-05 20:28:57 -04:00
|
|
|
inboundInfo.SpeedLimiter.Delete(fmt.Sprintf("%s|%s|%d", tag,
|
2022-08-12 06:02:06 -04:00
|
|
|
(updatedUserList)[i].V2rayUser.Email, (updatedUserList)[i].UID)) // Delete old limiter bucket
|
2022-06-01 13:35:41 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("no such inbound in limiter: %s", tag)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Limiter) DeleteInboundLimiter(tag string) error {
|
|
|
|
l.InboundInfo.Delete(tag)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-05 20:28:57 -04:00
|
|
|
type UserIpList struct {
|
|
|
|
Uid int `json:"Uid"`
|
|
|
|
IpList []string `json:"Ips"`
|
2022-07-28 11:00:05 -04:00
|
|
|
}
|
|
|
|
|
2022-09-05 20:28:57 -04:00
|
|
|
func (l *Limiter) ListOnlineUserIp(tag string) ([]UserIpList, error) {
|
2022-06-01 13:35:41 -04:00
|
|
|
if value, ok := l.InboundInfo.Load(tag); ok {
|
|
|
|
inboundInfo := value.(*InboundInfo)
|
2022-09-05 20:28:57 -04:00
|
|
|
onlineUser := make([]UserIpList, 0)
|
2022-07-28 11:00:05 -04:00
|
|
|
var ipMap *sync.Map
|
2022-06-01 13:35:41 -04:00
|
|
|
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
|
2022-07-28 11:00:05 -04:00
|
|
|
ipMap = value.(*sync.Map)
|
|
|
|
var ip []string
|
|
|
|
ipMap.Range(func(key, v interface{}) bool {
|
|
|
|
if v.(bool) {
|
|
|
|
ip = append(ip, key.(string))
|
|
|
|
}
|
2022-06-01 13:35:41 -04:00
|
|
|
return true
|
|
|
|
})
|
2022-07-28 11:00:05 -04:00
|
|
|
if len(ip) > 0 {
|
|
|
|
if u, ok := inboundInfo.UserInfo.Load(key.(string)); ok {
|
2022-09-05 20:28:57 -04:00
|
|
|
onlineUser = append(onlineUser, UserIpList{
|
|
|
|
Uid: u.(UserInfo).UID,
|
|
|
|
IpList: ip,
|
2022-07-28 11:00:05 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2022-06-01 13:35:41 -04:00
|
|
|
return true
|
|
|
|
})
|
2022-07-28 11:00:05 -04:00
|
|
|
if len(onlineUser) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2022-08-12 06:02:06 -04:00
|
|
|
return onlineUser, nil
|
2022-06-01 13:35:41 -04:00
|
|
|
} else {
|
|
|
|
return nil, fmt.Errorf("no such inbound in limiter: %s", tag)
|
|
|
|
}
|
2022-07-28 11:00:05 -04:00
|
|
|
}
|
|
|
|
|
2022-09-05 20:28:57 -04:00
|
|
|
func (l *Limiter) UpdateOnlineUserIP(tag string, userIpList []UserIpList) {
|
2022-07-28 11:00:05 -04:00
|
|
|
if v, ok := l.InboundInfo.Load(tag); ok {
|
|
|
|
inboundInfo := v.(*InboundInfo)
|
|
|
|
//Clear old IP
|
|
|
|
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
|
|
|
|
inboundInfo.UserOnlineIP.Delete(key)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
// Update User Online IP
|
2022-08-12 06:02:06 -04:00
|
|
|
for i := range userIpList {
|
2022-07-28 11:00:05 -04:00
|
|
|
ipMap := new(sync.Map)
|
2022-09-05 20:28:57 -04:00
|
|
|
for _, userIp := range (userIpList)[i].IpList {
|
2022-07-28 11:00:05 -04:00
|
|
|
ipMap.Store(userIp, false)
|
|
|
|
}
|
2022-08-12 06:02:06 -04:00
|
|
|
inboundInfo.UserOnlineIP.Store((userIpList)[i].Uid, ipMap)
|
2022-07-28 11:00:05 -04:00
|
|
|
}
|
2022-09-05 20:28:57 -04:00
|
|
|
inboundInfo.SpeedLimiter.Range(func(key, value interface{}) bool {
|
|
|
|
if _, exists := inboundInfo.UserOnlineIP.Load(key.(string)); !exists {
|
|
|
|
inboundInfo.SpeedLimiter.Delete(key.(string))
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
2022-07-28 11:00:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-05 20:28:57 -04:00
|
|
|
func (l *Limiter) ClearOnlineUserIpAndSpeedLimiter(tag string) {
|
2022-07-28 11:00:05 -04:00
|
|
|
if v, ok := l.InboundInfo.Load(tag); ok {
|
|
|
|
inboundInfo := v.(*InboundInfo)
|
2022-09-05 20:28:57 -04:00
|
|
|
inboundInfo.SpeedLimiter.Range(func(key, value interface{}) bool {
|
|
|
|
if _, exists := inboundInfo.UserOnlineIP.Load(key.(string)); !exists {
|
|
|
|
inboundInfo.SpeedLimiter.Delete(key.(string))
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
2022-07-28 11:00:05 -04:00
|
|
|
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
|
|
|
|
inboundInfo.UserOnlineIP.Delete(key)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
}
|
2022-06-01 13:35:41 -04:00
|
|
|
}
|
|
|
|
|
2022-09-05 20:28:57 -04:00
|
|
|
func (l *Limiter) CheckSpeedAndDeviceLimit(tag string, email string, ip string) (speedLimiter *ratelimit.Bucket, SpeedLimit bool, Reject bool) {
|
2022-06-01 13:35:41 -04:00
|
|
|
if value, ok := l.InboundInfo.Load(tag); ok {
|
|
|
|
inboundInfo := value.(*InboundInfo)
|
|
|
|
nodeLimit := inboundInfo.NodeSpeedLimit
|
|
|
|
var userLimit uint64 = 0
|
2022-06-06 03:23:40 -04:00
|
|
|
var deviceLimit = 0
|
2022-06-01 13:35:41 -04:00
|
|
|
if v, ok := inboundInfo.UserInfo.Load(email); ok {
|
|
|
|
u := v.(UserInfo)
|
|
|
|
userLimit = u.SpeedLimit
|
|
|
|
deviceLimit = u.DeviceLimit
|
|
|
|
}
|
|
|
|
ipMap := new(sync.Map)
|
2022-07-28 11:00:05 -04:00
|
|
|
ipMap.Store(ip, true)
|
2022-06-01 13:35:41 -04:00
|
|
|
// If any device is online
|
|
|
|
if v, ok := inboundInfo.UserOnlineIP.LoadOrStore(email, ipMap); ok {
|
|
|
|
ipMap := v.(*sync.Map)
|
|
|
|
// If this ip is a new device
|
2022-07-28 11:00:05 -04:00
|
|
|
if online, ok := ipMap.LoadOrStore(ip, true); !ok {
|
2022-06-01 13:35:41 -04:00
|
|
|
counter := 0
|
|
|
|
ipMap.Range(func(key, value interface{}) bool {
|
|
|
|
counter++
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if counter > deviceLimit && deviceLimit > 0 {
|
|
|
|
ipMap.Delete(ip)
|
|
|
|
return nil, false, true
|
|
|
|
}
|
2022-07-28 11:00:05 -04:00
|
|
|
} else {
|
|
|
|
if !online.(bool) {
|
|
|
|
ipMap.Store(ip, true)
|
|
|
|
}
|
2022-06-01 13:35:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
limit := determineRate(nodeLimit, userLimit) // If need the Speed limit
|
|
|
|
if limit > 0 {
|
2022-06-02 13:22:56 -04:00
|
|
|
limiter := ratelimit.NewBucketWithQuantum(time.Second, int64(limit), int64(limit)) // Byte/s
|
2022-09-05 20:28:57 -04:00
|
|
|
if v, ok := inboundInfo.SpeedLimiter.LoadOrStore(email, limiter); ok {
|
2022-06-01 13:35:41 -04:00
|
|
|
bucket := v.(*ratelimit.Bucket)
|
|
|
|
return bucket, true, false
|
|
|
|
} else {
|
|
|
|
return limiter, true, false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return nil, false, false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newError("Get Inbound Limiter information failed").AtDebug().WriteToLog()
|
|
|
|
return nil, false, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-16 01:04:33 -04:00
|
|
|
type Writer struct {
|
|
|
|
writer buf.Writer
|
|
|
|
limiter *ratelimit.Bucket
|
|
|
|
w io.Writer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Limiter) RateWriter(writer buf.Writer, limiter *ratelimit.Bucket) buf.Writer {
|
|
|
|
return &Writer{
|
|
|
|
writer: writer,
|
|
|
|
limiter: limiter,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Writer) Close() error {
|
|
|
|
return common.Close(w.writer)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|
|
|
w.limiter.Wait(int64(mb.Len()))
|
|
|
|
return w.writer.WriteMultiBuffer(mb)
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:35:41 -04:00
|
|
|
// determineRate returns the minimum non-zero rate
|
|
|
|
func determineRate(nodeLimit, userLimit uint64) (limit uint64) {
|
|
|
|
if nodeLimit == 0 || userLimit == 0 {
|
|
|
|
if nodeLimit > userLimit {
|
|
|
|
return nodeLimit
|
|
|
|
} else if nodeLimit < userLimit {
|
|
|
|
return userLimit
|
|
|
|
} else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if nodeLimit > userLimit {
|
|
|
|
return userLimit
|
|
|
|
} else if nodeLimit < userLimit {
|
|
|
|
return nodeLimit
|
|
|
|
} else {
|
|
|
|
return nodeLimit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|