V2bX/node/node.go

429 lines
11 KiB
Go
Raw Normal View History

package node
2022-06-01 13:35:41 -04:00
import (
"fmt"
"github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/conf"
"github.com/Yuzuki616/V2bX/core"
"github.com/Yuzuki616/V2bX/core/app/dispatcher"
"github.com/Yuzuki616/V2bX/node/legoCmd"
"github.com/go-resty/resty/v2"
"github.com/goccy/go-json"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/task"
2022-06-01 13:35:41 -04:00
"log"
"math"
2022-06-05 01:19:39 -04:00
"reflect"
2022-06-04 00:05:46 -04:00
"runtime"
2022-06-01 13:35:41 -04:00
"time"
)
type Node struct {
server *core.Core
config *conf.ControllerConfig
clientInfo panel.ClientInfo
apiClient panel.Panel
nodeInfo *panel.NodeInfo
2022-06-01 13:35:41 -04:00
Tag string
userList []panel.UserInfo
2022-06-01 13:35:41 -04:00
nodeInfoMonitorPeriodic *task.Periodic
userReportPeriodic *task.Periodic
onlineIpReportPeriodic *task.Periodic
2022-06-01 13:35:41 -04:00
}
// New return a Node service with default parameters.
func New(server *core.Core, api panel.Panel, config *conf.ControllerConfig) *Node {
controller := &Node{
2022-06-01 13:35:41 -04:00
server: server,
config: config,
apiClient: api,
}
return controller
}
// Start implement the Start() function of the service interface
func (c *Node) Start() error {
2022-06-01 13:35:41 -04:00
c.clientInfo = c.apiClient.Describe()
// First fetch Node Info
newNodeInfo, err := c.apiClient.GetNodeInfo()
if err != nil {
return err
}
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
// Add new tag
err = c.addNewTag(newNodeInfo)
if err != nil {
log.Panic(err)
return err
}
// Update user
userInfo, err := c.apiClient.GetUserList()
if err != nil {
return err
}
err = c.addNewUser(userInfo, newNodeInfo)
if err != nil {
return err
}
//sync controller userList
c.userList = userInfo
if err := c.server.AddInboundLimiter(c.Tag, c.nodeInfo, userInfo); err != nil {
2022-06-02 13:22:56 -04:00
log.Print(err)
}
2022-06-01 13:35:41 -04:00
// Add Rule Manager
if !c.config.DisableGetRule {
2022-06-12 09:10:20 -04:00
if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil {
2022-06-01 13:35:41 -04:00
log.Printf("Get rule list filed: %s", err)
} else if len(ruleList) > 0 {
if err := c.server.UpdateRule(c.Tag, ruleList); err != nil {
2022-06-01 13:35:41 -04:00
log.Print(err)
}
if len(protocolRule) > 0 {
if err := c.server.UpdateProtocolRule(c.Tag, protocolRule); err != nil {
2022-06-12 09:10:20 -04:00
log.Print(err)
}
}
2022-06-01 13:35:41 -04:00
}
}
c.nodeInfoMonitorPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.nodeInfoMonitor,
}
c.userReportPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.userInfoMonitor,
}
log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId)
// delay to start nodeInfoMonitor
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.nodeInfoMonitorPeriodic.Start()
}()
log.Printf("[%s: %d] Start report node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId)
// delay to start userReport
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.userReportPeriodic.Start()
}()
2022-08-19 10:42:51 -04:00
if c.config.EnableIpRecorder {
c.onlineIpReportPeriodic = &task.Periodic{
Interval: time.Duration(c.config.IpRecorderConfig.Periodic) * time.Second,
Execute: c.onlineIpReport,
}
2022-08-19 10:55:47 -04:00
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.onlineIpReportPeriodic.Start()
}()
log.Printf("[%s: %d] Start report online ip", c.nodeInfo.NodeType, c.nodeInfo.NodeId)
}
2022-06-05 01:19:39 -04:00
runtime.GC()
2022-06-01 13:35:41 -04:00
return nil
}
// Close implement the Close() function of the service interface
func (c *Node) Close() error {
2022-06-01 13:35:41 -04:00
if c.nodeInfoMonitorPeriodic != nil {
err := c.nodeInfoMonitorPeriodic.Close()
if err != nil {
log.Panicf("node info periodic close failed: %s", err)
}
}
if c.nodeInfoMonitorPeriodic != nil {
err := c.userReportPeriodic.Close()
if err != nil {
log.Panicf("user report periodic close failed: %s", err)
}
}
if c.onlineIpReportPeriodic != nil {
err := c.onlineIpReportPeriodic.Close()
if err != nil {
log.Panicf("online ip report periodic close failed: %s", err)
}
}
2022-06-01 13:35:41 -04:00
return nil
}
func (c *Node) nodeInfoMonitor() (err error) {
2022-06-01 13:35:41 -04:00
// First fetch Node Info
newNodeInfo, err := c.apiClient.GetNodeInfo()
if err != nil {
log.Print(err)
return nil
}
var nodeInfoChanged = false
// If nodeInfo changed
2022-06-02 13:22:56 -04:00
if newNodeInfo != nil {
2022-06-06 05:07:21 -04:00
if c.nodeInfo.SS == nil || !reflect.DeepEqual(c.nodeInfo.SS, newNodeInfo.SS) {
2022-06-05 01:19:39 -04:00
// 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.server.DeleteInboundLimiter(oldtag); err != nil {
2022-06-05 01:19:39 -04:00
log.Print(err)
return nil
}
2022-06-01 13:35:41 -04:00
}
}
// Check Rule
if !c.config.DisableGetRule {
2022-06-12 09:10:20 -04:00
if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil {
2022-06-01 13:35:41 -04:00
log.Printf("Get rule list filed: %s", err)
} else if len(ruleList) > 0 {
if err := c.server.UpdateRule(c.Tag, ruleList); err != nil {
2022-06-01 13:35:41 -04:00
log.Print(err)
}
if len(protocolRule) > 0 {
if err := c.server.UpdateProtocolRule(c.Tag, protocolRule); err != nil {
2022-06-12 09:10:20 -04:00
log.Print(err)
}
}
2022-06-01 13:35:41 -04:00
}
}
// Check Cert
2022-06-04 00:05:46 -04:00
if c.nodeInfo.EnableTls && c.config.CertConfig.CertMode != "none" &&
(c.config.CertConfig.CertMode == "dns" || c.config.CertConfig.CertMode == "http") {
lego, err := legoCmd.New()
2022-06-01 13:35:41 -04:00
if err != nil {
log.Print(err)
}
// Core-core supports the OcspStapling certification hot renew
2022-06-04 00:05:46 -04:00
_, _, err = lego.RenewCert(c.config.CertConfig.CertDomain, c.config.CertConfig.Email,
c.config.CertConfig.CertMode, c.config.CertConfig.Provider, c.config.CertConfig.DNSEnv)
2022-06-01 13:35:41 -04:00
if err != nil {
log.Print(err)
}
}
2022-06-04 00:05:46 -04:00
// Update User
newUserInfo, err := c.apiClient.GetUserList()
if err != nil {
log.Print(err)
return nil
}
2022-06-01 13:35:41 -04:00
if nodeInfoChanged {
2022-06-09 09:19:57 -04:00
c.userList = newUserInfo
2022-06-05 22:08:02 -04:00
newUserInfo = nil
err = c.addNewUser(c.userList, newNodeInfo)
2022-06-01 13:35:41 -04:00
if err != nil {
log.Print(err)
return nil
}
2022-06-05 01:19:39 -04:00
newNodeInfo = nil
2022-06-04 00:05:46 -04:00
// Add Limiter
if err := c.server.AddInboundLimiter(c.Tag, c.nodeInfo, c.userList); err != nil {
2022-06-04 00:05:46 -04:00
log.Print(err)
return nil
}
2022-06-05 01:19:39 -04:00
runtime.GC()
2022-06-09 09:19:57 -04:00
} else {
2022-06-01 13:35:41 -04:00
deleted, added := compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i := range deleted {
2022-06-02 13:22:56 -04:00
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag,
(deleted)[i].GetUserEmail(),
(deleted)[i].UID)
2022-06-01 13:35:41 -04:00
}
err := c.server.RemoveUsers(deletedEmail, c.Tag)
2022-06-01 13:35:41 -04:00
if err != nil {
log.Print(err)
}
}
if len(added) > 0 {
2022-08-14 00:15:19 -04:00
err = c.addNewUser(added, c.nodeInfo)
2022-06-01 13:35:41 -04:00
if err != nil {
log.Print(err)
}
// Update Limiter
if err := c.server.UpdateInboundLimiter(c.Tag, c.nodeInfo, added); err != nil {
2022-06-01 13:35:41 -04:00
log.Print(err)
}
}
log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeId,
len(deleted), len(added))
2022-06-04 00:05:46 -04:00
c.userList = newUserInfo
2022-06-05 01:19:39 -04:00
newUserInfo = nil
runtime.GC()
2022-06-01 13:35:41 -04:00
}
return nil
}
func (c *Node) removeOldTag(oldtag string) (err error) {
err = c.server.RemoveInbound(oldtag)
2022-06-01 13:35:41 -04:00
if err != nil {
return err
}
err = c.server.RemoveOutbound(oldtag)
2022-06-01 13:35:41 -04:00
if err != nil {
return err
}
return nil
}
func (c *Node) addNewTag(newNodeInfo *panel.NodeInfo) (err error) {
2022-06-01 13:35:41 -04:00
inboundConfig, err := InboundBuilder(c.config, newNodeInfo, c.Tag)
if err != nil {
return err
}
err = c.server.AddInbound(inboundConfig)
2022-06-01 13:35:41 -04:00
if err != nil {
return err
}
outBoundConfig, err := OutboundBuilder(c.config, newNodeInfo, c.Tag)
if err != nil {
return err
}
err = c.server.AddOutbound(outBoundConfig)
2022-06-01 13:35:41 -04:00
if err != nil {
return err
}
return nil
}
func (c *Node) addNewUser(userInfo []panel.UserInfo, nodeInfo *panel.NodeInfo) (err error) {
2022-06-01 13:35:41 -04:00
users := make([]*protocol.User, 0)
if nodeInfo.NodeType == "V2ray" {
if nodeInfo.EnableVless {
2022-06-02 13:22:56 -04:00
users = c.buildVlessUsers(userInfo)
2022-06-01 13:35:41 -04:00
} else {
alterID := 0
alterID = (userInfo)[0].V2rayUser.AlterId
2022-06-01 13:35:41 -04:00
if alterID >= 0 && alterID < math.MaxUint16 {
2022-06-02 13:22:56 -04:00
users = c.buildVmessUsers(userInfo, uint16(alterID))
2022-06-01 13:35:41 -04:00
} else {
2022-06-02 13:22:56 -04:00
users = c.buildVmessUsers(userInfo, 0)
2022-06-01 13:35:41 -04:00
return fmt.Errorf("AlterID should between 0 to 1<<16 - 1, set it to 0 for now")
}
}
} else if nodeInfo.NodeType == "Trojan" {
2022-06-02 13:22:56 -04:00
users = c.buildTrojanUsers(userInfo)
2022-06-01 13:35:41 -04:00
} else if nodeInfo.NodeType == "Shadowsocks" {
users = c.buildSSUsers(userInfo, getCipherFromString(nodeInfo.SS.CypherMethod))
2022-06-01 13:35:41 -04:00
} else {
return fmt.Errorf("unsupported node type: %s", nodeInfo.NodeType)
}
err = c.server.AddUsers(users, c.Tag)
2022-06-01 13:35:41 -04:00
if err != nil {
return err
}
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(userInfo))
2022-06-01 13:35:41 -04:00
return nil
}
func compareUserList(old, new []panel.UserInfo) (deleted, added []panel.UserInfo) {
2022-06-09 09:19:57 -04:00
tmp := map[string]struct{}{}
tmp2 := map[string]struct{}{}
for i := range old {
tmp[(old)[i].GetUserEmail()] = struct{}{}
2022-06-01 13:35:41 -04:00
}
l := len(tmp)
for i := range new {
e := (new)[i].GetUserEmail()
2022-06-09 09:40:23 -04:00
tmp[e] = struct{}{}
tmp2[e] = struct{}{}
2022-06-01 13:35:41 -04:00
if l != len(tmp) {
added = append(added, (new)[i])
2022-06-01 13:35:41 -04:00
l++
}
}
2022-06-02 13:22:56 -04:00
tmp = nil
l = len(tmp2)
for i := range old {
tmp2[(old)[i].GetUserEmail()] = struct{}{}
2022-06-02 13:22:56 -04:00
if l != len(tmp2) {
deleted = append(deleted, (old)[i])
2022-06-01 13:35:41 -04:00
l++
}
}
return deleted, added
}
func (c *Node) userInfoMonitor() (err error) {
2022-06-01 13:35:41 -04:00
// Get User traffic
userTraffic := make([]panel.UserTraffic, 0)
for i := range c.userList {
2022-09-07 05:26:12 -04:00
up, down := c.server.GetUserTraffic(c.buildUserTag(&(c.userList)[i]), true)
2022-06-01 13:35:41 -04:00
if up > 0 || down > 0 {
userTraffic = append(userTraffic, panel.UserTraffic{
UID: (c.userList)[i].UID,
2022-06-01 13:35:41 -04:00
Upload: up,
Download: down})
}
}
if len(userTraffic) > 0 && !c.config.DisableUploadTraffic {
err = c.apiClient.ReportUserTraffic(userTraffic)
2022-06-01 13:35:41 -04:00
if err != nil {
log.Print(err)
} else {
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(userTraffic))
2022-06-01 13:35:41 -04:00
}
}
userTraffic = nil
2022-08-19 10:42:51 -04:00
if !c.config.EnableIpRecorder {
2022-09-05 12:32:52 -04:00
c.server.ClearOnlineIp(c.Tag)
2022-08-19 10:42:51 -04:00
}
runtime.GC()
return nil
}
2022-06-01 13:35:41 -04:00
func (c *Node) onlineIpReport() (err error) {
2022-09-05 12:32:52 -04:00
onlineIp, err := c.server.ListOnlineIp(c.Tag)
2022-08-19 10:42:51 -04:00
if err != nil {
log.Print(err)
return nil
}
rsp, err := resty.New().SetTimeout(time.Duration(c.config.IpRecorderConfig.Timeout) * time.Second).
R().
SetBody(onlineIp).
Post(c.config.IpRecorderConfig.Url +
"/api/v1/SyncOnlineIp?token=" +
c.config.IpRecorderConfig.Token)
if err != nil {
log.Print(err)
2022-09-05 12:32:52 -04:00
c.server.ClearOnlineIp(c.Tag)
2022-08-19 10:42:51 -04:00
return nil
}
log.Printf("[Node: %d] Report %d online ip", c.nodeInfo.NodeId, len(onlineIp))
if rsp.StatusCode() == 200 {
2022-09-05 20:28:57 -04:00
onlineIp = []dispatcher.UserIpList{}
2022-08-19 10:42:51 -04:00
err := json.Unmarshal(rsp.Body(), &onlineIp)
if err != nil {
log.Print(err)
2022-09-05 12:32:52 -04:00
c.server.ClearOnlineIp(c.Tag)
return nil
}
2022-09-05 20:59:47 -04:00
if c.config.IpRecorderConfig.EnableIpSync {
c.server.UpdateOnlineIp(c.Tag, onlineIp)
log.Printf("[Node: %d] Updated %d online ip", c.nodeInfo.NodeId, len(onlineIp))
}
2022-06-01 13:35:41 -04:00
} else {
2022-09-05 12:32:52 -04:00
c.server.ClearOnlineIp(c.Tag)
2022-06-01 13:35:41 -04:00
}
return nil
}
func (c *Node) buildNodeTag() string {
2022-06-01 13:35:41 -04:00
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.NodeId)
}