diff --git a/api/panel/panel.go b/api/panel/panel.go index c298d05..9a4a0e9 100644 --- a/api/panel/panel.go +++ b/api/panel/panel.go @@ -25,6 +25,8 @@ type Client struct { userEtag string responseBodyHash string LastReportOnline map[int]int + UserList *UserListBody + AliveMap *AliveMap } func New(c *conf.ApiConfig) (*Client, error) { @@ -71,5 +73,7 @@ func New(c *conf.ApiConfig) (*Client, error) { APIHost: c.APIHost, NodeType: c.NodeType, NodeId: c.NodeID, + UserList: &UserListBody{}, + AliveMap: &AliveMap{}, }, nil } diff --git a/api/panel/user.go b/api/panel/user.go index 88286ab..8bbf9e7 100644 --- a/api/panel/user.go +++ b/api/panel/user.go @@ -16,7 +16,6 @@ type UserInfo struct { Uuid string `json:"uuid"` SpeedLimit int `json:"speed_limit"` DeviceLimit int `json:"device_limit"` - AliveIp int `json:"alive_ip"` } type UserListBody struct { @@ -24,8 +23,12 @@ type UserListBody struct { Users []UserInfo `json:"users"` } -// GetUserList will pull user form sspanel -func (c *Client) GetUserList() (UserList []UserInfo, err error) { +type AliveMap struct { + Alive map[int]int `json:"alive"` +} + +// GetUserList will pull user from v2board +func (c *Client) GetUserList() ([]UserInfo, error) { const path = "/api/v1/server/UniProxy/user" r, err := c.client.R(). SetHeader("If-None-Match", c.userEtag). @@ -34,52 +37,44 @@ func (c *Client) GetUserList() (UserList []UserInfo, err error) { if err = c.checkResponse(r, path, err); err != nil { return nil, err } - if r != nil { - defer func() { - if r.RawBody() != nil { - r.RawBody().Close() - } - }() - if r.StatusCode() == 304 { - return nil, nil - } + defer r.RawResponse.Body.Close() } else { return nil, fmt.Errorf("received nil response") } - var userList *UserListBody - if err != nil { - return nil, fmt.Errorf("read body error: %s", err) - } - if err := json.Unmarshal(r.Body(), &userList); err != nil { - return nil, fmt.Errorf("unmarshal userlist error: %s", err) - } - c.userEtag = r.Header().Get("ETag") - var userinfos []UserInfo - var deviceLimit, localDeviceLimit int = 0, 0 - for _, user := range userList.Users { - // If there is still device available, add the user - if user.DeviceLimit > 0 && user.AliveIp > 0 { - lastOnline := 0 - if v, ok := c.LastReportOnline[user.Id]; ok { - lastOnline = v - } - // If there are any available device. - localDeviceLimit = user.DeviceLimit - user.AliveIp + lastOnline - if localDeviceLimit > 0 { - deviceLimit = localDeviceLimit - } else if lastOnline > 0 { - deviceLimit = lastOnline - } else { - continue - } + if r.StatusCode() == 304 { + return nil, nil + } else { + if err := json.Unmarshal(r.Body(), c.UserList); err != nil { + return nil, fmt.Errorf("unmarshal user list error: %w", err) } - user.DeviceLimit = deviceLimit - userinfos = append(userinfos, user) + c.userEtag = r.Header().Get("ETag") + } + return c.UserList.Users, nil +} + +// GetUserAlive will fetch the alive IPs for users +func (c *Client) GetUserAlive() (map[int]int, error) { + const path = "/api/v1/server/UniProxy/alivelist" + r, err := c.client.R(). + ForceContentType("application/json"). + Get(path) + if err = c.checkResponse(r, path, err); err != nil { + return nil, err } - return userinfos, nil + if r != nil { + defer r.RawResponse.Body.Close() + } else { + return nil, fmt.Errorf("received nil response") + } + + if err := json.Unmarshal(r.Body(), c.AliveMap); err != nil { + return nil, fmt.Errorf("unmarshal user alive list error: %s", err) + } + + return c.AliveMap.Alive, nil } type UserTraffic struct { diff --git a/core/xray/ss.go b/core/xray/ss.go index d6efdee..42e265a 100644 --- a/core/xray/ss.go +++ b/core/xray/ss.go @@ -38,6 +38,8 @@ func buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey keyLength = 16 case "2022-blake3-aes-256-gcm": keyLength = 32 + case "2022-blake3-chacha20-poly1305": + keyLength = 32 } ssAccount := &shadowsocks_2022.User{ Level: 0, diff --git a/limiter/limiter.go b/limiter/limiter.go index d421500..47479af 100644 --- a/limiter/limiter.go +++ b/limiter/limiter.go @@ -21,13 +21,13 @@ var limiter map[string]*Limiter func Init() { limiter = map[string]*Limiter{} c := task.Periodic{ - Interval: time.Minute * 2, + Interval: time.Minute * 3, Execute: ClearOnlineIP, } go func() { log.WithField("Type", "Limiter"). Debug("ClearOnlineIP started") - time.Sleep(time.Minute * 2) + time.Sleep(time.Minute * 3) _ = c.Start() }() } @@ -41,6 +41,7 @@ type Limiter struct { 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:Id, value:alive_ip } type UserLimitInfo struct { @@ -52,13 +53,14 @@ type UserLimitInfo struct { OverLimit bool } -func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limiter { +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, } uuidmap := make(map[string]int) for i := range users { @@ -163,18 +165,23 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool // 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 { - counter := 0 - ipMap.Range(func(key, value interface{}) bool { - counter++ - return true - }) - if counter > deviceLimit && deviceLimit > 0 { - ipMap.Delete(ip) + if deviceLimit > 0 { + if deviceLimit <= aliveIp { + ipMap.Delete(ip) + return nil, true + } + } + } + } else { + if deviceLimit > 0 { + if deviceLimit <= aliveIp { + l.UserOnlineIP.Delete(taguuid) return nil, true } } @@ -218,23 +225,3 @@ type UserIpList struct { Uid int `json:"Uid"` IpList []string `json:"Ips"` } - -func determineDeviceLimit(nodeLimit, userLimit int) (limit int) { - 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 - } - } -} diff --git a/node/controller.go b/node/controller.go index e8aa29d..9947b06 100644 --- a/node/controller.go +++ b/node/controller.go @@ -19,6 +19,7 @@ type Controller struct { limiter *limiter.Limiter traffic map[string]int64 userList []panel.UserInfo + aliveMap map[int]int info *panel.NodeInfo nodeInfoMonitorPeriodic *task.Task userReportPeriodic *task.Task @@ -54,6 +55,10 @@ func (c *Controller) Start() error { if len(c.userList) == 0 { return errors.New("add users error: not have any user") } + c.aliveMap, err = c.apiClient.GetUserAlive() + if err != nil { + return fmt.Errorf("failed to get user alive list: %s", err) + } if len(c.Options.Name) == 0 { c.tag = c.buildNodeTag(node) } else { @@ -61,7 +66,7 @@ func (c *Controller) Start() error { } // add limiter - l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList) + l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, c.aliveMap) // add rule limiter if err = l.UpdateRule(&node.Rules); err != nil { return fmt.Errorf("update rule error: %s", err) diff --git a/node/task.go b/node/task.go index 51c12f2..5cee41d 100644 --- a/node/task.go +++ b/node/task.go @@ -68,6 +68,11 @@ func (c *Controller) nodeInfoMonitor() (err error) { }).Error("Get user list failed") return nil } + // get user alive + newA, err := c.apiClient.GetUserAlive() + if err != nil { + return err + } if newN != nil { c.info = newN // nodeInfo changed @@ -92,7 +97,7 @@ func (c *Controller) nodeInfoMonitor() (err error) { // Remove Old limiter limiter.DeleteLimiter(c.tag) // Add new Limiter - l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList) + l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, newA) c.limiter = l } // Update rule @@ -154,7 +159,10 @@ func (c *Controller) nodeInfoMonitor() (err error) { // exit return nil } - + // update alive list + if newA != nil { + c.limiter.AliveList = newA + } // node no changed, check users if len(newU) == 0 { return nil