diff --git a/api/api.go b/api/api.go index c210255..b5c4518 100644 --- a/api/api.go +++ b/api/api.go @@ -50,6 +50,7 @@ type Client struct { RemoteRuleCache *Rule access sync.Mutex NodeInfoRspMd5 [16]byte + NodeRuleRspMd5 [16]byte UserListCheckNum int } diff --git a/api/node.go b/api/node.go index 6436cb9..200e58a 100644 --- a/api/node.go +++ b/api/node.go @@ -2,6 +2,7 @@ package api import ( "bufio" + "bytes" md52 "crypto/md5" "fmt" "github.com/go-resty/resty/v2" @@ -76,7 +77,7 @@ type SSConfig struct { type V2rayConfig struct { Inbounds []conf.InboundDetourConfig `json:"inbounds"` Routing *struct { - Rules []Rule `json:"rules"` + Rules *json.RawMessage `json:"rules"` } `json:"routing"` } @@ -104,10 +105,6 @@ func (c *Client) GetNodeInfo() (nodeInfo *NodeInfo, err error) { switch c.NodeType { case "V2ray": path = "/api/v1/server/Deepbwork/config" - res, err = c.client.R(). - SetQueryParam("local_port", "1"). - ForceContentType("application/json"). - Get(path) case "Trojan": path = "/api/v1/server/TrojanTidalab/config" case "Shadowsocks": @@ -119,13 +116,6 @@ func (c *Client) GetNodeInfo() (nodeInfo *NodeInfo, err error) { default: return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType) } - md := md52.Sum(res.Body()) - if c.NodeInfoRspMd5 != [16]byte{} { - if c.NodeInfoRspMd5 == md { - return nil, nil - } - } - c.NodeInfoRspMd5 = md res, err = c.client.R(). SetQueryParam("local_port", "1"). ForceContentType("application/json"). @@ -134,12 +124,25 @@ func (c *Client) GetNodeInfo() (nodeInfo *NodeInfo, err error) { if err != nil { return nil, err } + i := bytes.Index(res.Body(), []byte("outbo")) + md := md52.Sum(res.Body()[:i]) + nodeIsNotChange := false + if c.NodeInfoRspMd5 != [16]byte{} { + if c.NodeInfoRspMd5 == md { + nodeIsNotChange = true + } + } + c.NodeInfoRspMd5 = md c.access.Lock() defer c.access.Unlock() switch c.NodeType { case "V2ray": - nodeInfo, err = c.ParseV2rayNodeResponse(res.Body()) + md2 := md52.Sum(res.Body()[i:]) + nodeInfo, err = c.ParseV2rayNodeResponse(res.Body(), nodeIsNotChange, c.NodeRuleRspMd5 != md2) case "Trojan": + if nodeIsNotChange { + return nil, nil + } nodeInfo, err = c.ParseTrojanNodeResponse(res.Body()) } return nodeInfo, nil @@ -147,7 +150,7 @@ func (c *Client) GetNodeInfo() (nodeInfo *NodeInfo, err error) { func (c *Client) GetNodeRule() (*[]DetectRule, error) { ruleList := c.LocalRuleList - if c.NodeType != "V2ray" { + if c.NodeType != "V2ray" || c.RemoteRuleCache == nil { return &ruleList, nil } @@ -162,6 +165,7 @@ func (c *Client) GetNodeRule() (*[]DetectRule, error) { } ruleList = append(ruleList, ruleListItem) } + c.RemoteRuleCache = nil return &ruleList, nil } @@ -211,18 +215,24 @@ func (c *Client) ParseSSNodeResponse() (*NodeInfo, error) { } // ParseV2rayNodeResponse parse the response for the given nodeinfor format -func (c *Client) ParseV2rayNodeResponse(body []byte) (*NodeInfo, error) { +func (c *Client) ParseV2rayNodeResponse(body []byte, notParseNode, parseRule bool) (*NodeInfo, error) { node := &NodeInfo{V2ray: &V2rayConfig{}} err := json.Unmarshal(body, node.V2ray) if err != nil { return nil, fmt.Errorf("unmarshal nodeinfo error: %s", err) } + if parseRule { + json.Unmarshal(*node.V2ray.Routing.Rules, c.RemoteRuleCache) + } + node.V2ray.Routing = nil + if notParseNode { + return nil, nil + } node.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8) node.DeviceLimit = c.DeviceLimit node.NodeType = c.NodeType node.NodeId = c.NodeID - c.RemoteRuleCache = &node.V2ray.Routing.Rules[0] - node.V2ray.Routing = nil + if c.EnableXTLS { node.TLSType = "xtls" } else { diff --git a/api/user.go b/api/user.go index a3ee031..51aeeda 100644 --- a/api/user.go +++ b/api/user.go @@ -69,15 +69,18 @@ func (c *Client) GetUserList() (UserList *[]UserInfo, err error) { if err != nil { return nil, fmt.Errorf("unmarshal userlist error: %s", err) } - checkNum := userList.Data[len(userList.Data)-1].UID + - userList.Data[len(userList.Data)/2-1].UID + + l := len(userList.Data) + checkNum := userList.Data[l-1].UID + + userList.Data[l/2-1].UID + userList.Data[0].UID if c.UserListCheckNum != 0 { if c.UserListCheckNum == checkNum { return nil, nil } } - c.UserListCheckNum = userList.Data[len(userList.Data)-1].UID + c.UserListCheckNum = userList.Data[l-1].UID + + userList.Data[l/2-1].UID + + userList.Data[0].UID return &userList.Data, nil } diff --git a/service/controller/controller.go b/service/controller/controller.go index 44833e9..88f849a 100644 --- a/service/controller/controller.go +++ b/service/controller/controller.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "math" + "reflect" "runtime" "time" @@ -99,6 +100,7 @@ func (c *Controller) Start() error { time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second) _ = c.userReportPeriodic.Start() }() + runtime.GC() return nil } @@ -131,26 +133,28 @@ func (c *Controller) nodeInfoMonitor() (err error) { var nodeInfoChanged = false // If nodeInfo changed if newNodeInfo != nil { - // Remove old tag - oldtag := c.Tag - err := c.removeOldTag(oldtag) - if err != nil { - log.Print(err) - return nil - } - // Add new tag - c.nodeInfo = newNodeInfo - c.Tag = c.buildNodeTag() - err = c.addNewTag(newNodeInfo) - if err != nil { - log.Print(err) - return nil - } - nodeInfoChanged = true - // Remove Old limiter - if err = c.DeleteInboundLimiter(oldtag); err != nil { - log.Print(err) - return nil + if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) { + // Remove old tag + oldtag := c.Tag + err := c.removeOldTag(oldtag) + if err != nil { + log.Print(err) + return nil + } + // Add new tag + c.nodeInfo = newNodeInfo + c.Tag = c.buildNodeTag() + err = c.addNewTag(newNodeInfo) + if err != nil { + log.Print(err) + return nil + } + nodeInfoChanged = true + // Remove Old limiter + if err = c.DeleteInboundLimiter(oldtag); err != nil { + log.Print(err) + return nil + } } } @@ -158,7 +162,7 @@ func (c *Controller) nodeInfoMonitor() (err error) { if !c.config.DisableGetRule { if ruleList, err := c.apiClient.GetNodeRule(); err != nil { log.Printf("Get rule list filed: %s", err) - } else if len(*ruleList) > 0 { + } else if ruleList != nil { if err := c.UpdateRule(c.Tag, *ruleList); err != nil { log.Print(err) } @@ -185,9 +189,6 @@ func (c *Controller) nodeInfoMonitor() (err error) { log.Print(err) return nil } - if newUserInfo == nil { - return nil - } if nodeInfoChanged { if newUserInfo != nil { c.userList = newUserInfo @@ -197,12 +198,15 @@ func (c *Controller) nodeInfoMonitor() (err error) { log.Print(err) return nil } + newNodeInfo = nil // Add Limiter if err := c.AddInboundLimiter(c.Tag, newUserInfo); err != nil { log.Print(err) return nil } - } else { + newUserInfo = nil + runtime.GC() + } else if newUserInfo != nil { deleted, added := compareUserList(c.userList, newUserInfo) if len(deleted) > 0 { deletedEmail := make([]string, len(deleted)) @@ -229,8 +233,9 @@ func (c *Controller) nodeInfoMonitor() (err error) { log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(deleted), len(added)) c.userList = newUserInfo + newUserInfo = nil + runtime.GC() } - runtime.GC() return nil } @@ -361,11 +366,11 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []int) { func (c *Controller) userInfoMonitor() (err error) { // Get User traffic userTraffic := make([]api.UserTraffic, 0) - for _, user := range *c.userList { - up, down := c.getTraffic(c.buildUserTag(&user)) + for i := range *c.userList { + up, down := c.getTraffic(c.buildUserTag(&(*c.userList)[i])) if up > 0 || down > 0 { userTraffic = append(userTraffic, api.UserTraffic{ - UID: user.UID, + UID: (*c.userList)[i].UID, Upload: up, Download: down}) } @@ -389,6 +394,8 @@ func (c *Controller) userInfoMonitor() (err error) { } else { log.Printf("[%s: %d] Report %d illegal behaviors", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(*detectResult)) } + userTraffic = nil + runtime.GC() return nil } diff --git a/service/controller/inboundbuilder.go b/service/controller/inboundbuilder.go index 5232a49..e828b1e 100644 --- a/service/controller/inboundbuilder.go +++ b/service/controller/inboundbuilder.go @@ -16,7 +16,9 @@ import ( func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.InboundHandlerConfig, error) { var proxySetting interface{} if nodeInfo.NodeType == "V2ray" { - defer func() { nodeInfo.V2ray = nil }() + defer func() { + nodeInfo.V2ray = nil + }() if nodeInfo.EnableVless { nodeInfo.V2ray.Inbounds[0].Protocol = "vless" // Enable fallback @@ -40,7 +42,10 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I proxySetting = &conf.VMessInboundConfig{} } } else if nodeInfo.NodeType == "Trojan" { - defer func() { nodeInfo.V2ray = nil; nodeInfo.Trojan = nil }() + defer func() { + nodeInfo.V2ray = nil + nodeInfo.Trojan = nil + }() nodeInfo.V2ray = &api.V2rayConfig{} nodeInfo.V2ray.Inbounds = make([]conf.InboundDetourConfig, 1) nodeInfo.V2ray.Inbounds[0].Protocol = "trojan" @@ -63,6 +68,10 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I t := conf.TransportProtocol(nodeInfo.SS.TransportProtocol) nodeInfo.V2ray.Inbounds[0].StreamSetting = &conf.StreamConfig{Network: &t} } else if nodeInfo.NodeType == "Shadowsocks" { + defer func() { + nodeInfo.V2ray = nil + nodeInfo.SS = nil + }() defer func() { nodeInfo.V2ray = nil; nodeInfo.SS = nil }() nodeInfo.V2ray = &api.V2rayConfig{} nodeInfo.V2ray.Inbounds = []conf.InboundDetourConfig{{Protocol: "shadowsocks"}}