diff --git a/api/panel/node.go b/api/panel/node.go index 9343044..352fc8b 100644 --- a/api/panel/node.go +++ b/api/panel/node.go @@ -242,7 +242,7 @@ func (c *Client) ParseV2rayNodeResponse(body []byte, notParseNode, parseRule boo } if parseRule { c.RemoteRuleCache = []Rule{} - err := json.Unmarshal(node.V2ray.Routing.Rules, c.RemoteRuleCache) + err := json.Unmarshal(node.V2ray.Routing.Rules, &c.RemoteRuleCache) if err != nil { log.Println(err) } diff --git a/core/core.go b/core/core.go index acb9eb0..3502e09 100644 --- a/core/core.go +++ b/core/core.go @@ -150,18 +150,17 @@ func getCore(v2bXConfig *conf.Conf) *core.Instance { } // Start the Core -func (p *Core) Start() { +func (p *Core) Start() error { p.access.Lock() defer p.access.Unlock() - log.Print("Start the panel..") if err := p.Server.Start(); err != nil { - log.Panicf("Failed to start instance: %s", err) + return err } p.shm = p.Server.GetFeature(statsFeature.ManagerType()).(statsFeature.Manager) p.ihm = p.Server.GetFeature(inbound.ManagerType()).(inbound.Manager) p.ohm = p.Server.GetFeature(outbound.ManagerType()).(outbound.Manager) p.dispatcher = p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher) - return + return nil } // Close the core diff --git a/main.go b/main.go index 12ee90a..12d769c 100644 --- a/main.go +++ b/main.go @@ -35,7 +35,7 @@ func startNodes(nodes []*conf.NodeConfig, core *core.Core) error { // Register controller service err := node.New(core, apiClient, nodes[i].ControllerConfig).Start() if err != nil { - return fmt.Errorf("start node controller error: %v", err) + return err } } return nil @@ -52,12 +52,16 @@ func main() { if err != nil { log.Panicf("can't unmarshal config file: %s \n", err) } + log.Println("Start V2bX...") x := core.New(config) - x.Start() + err = x.Start() + if err != nil { + log.Panicf("Failed to start core: %s", err) + } defer x.Close() err = startNodes(config.NodesConfig, x) if err != nil { - log.Panicf("run nodes error: %v", err) + log.Panicf("run nodes error: %s", err) } //Explicitly triggering GC to remove garbage from config loading. runtime.GC() diff --git a/node/inboundbuilder.go b/node/inbound.go similarity index 92% rename from node/inboundbuilder.go rename to node/inbound.go index 43d7788..e296ab4 100644 --- a/node/inboundbuilder.go +++ b/node/inbound.go @@ -13,17 +13,19 @@ import ( coreConf "github.com/xtls/xray-core/infra/conf" ) -//InboundBuilder build Inbound config for different protocol -func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.InboundHandlerConfig, error) { +// buildInbound build Inbound config for different protocol +func buildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.InboundHandlerConfig, error) { var proxySetting interface{} if nodeInfo.NodeType == "V2ray" { defer func() { + //Clear v2ray config nodeInfo.V2ray = nil }() if nodeInfo.EnableVless { + //Set vless nodeInfo.V2ray.Inbounds[0].Protocol = "vless" - // Enable fallback if config.EnableFallback { + // Set fallback fallbackConfigs, err := buildVlessFallbacks(config.FallBackConfigs) if err == nil { proxySetting = &coreConf.VLessInboundConfig{ @@ -39,19 +41,21 @@ func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag } } } else { + // Set vmess nodeInfo.V2ray.Inbounds[0].Protocol = "vmess" proxySetting = &coreConf.VMessInboundConfig{} } } else if nodeInfo.NodeType == "Trojan" { defer func() { + //clear trojan and v2ray config nodeInfo.V2ray = nil nodeInfo.Trojan = nil }() nodeInfo.V2ray = &panel.V2rayConfig{} nodeInfo.V2ray.Inbounds = make([]coreConf.InboundDetourConfig, 1) nodeInfo.V2ray.Inbounds[0].Protocol = "trojan" - // Enable fallback if config.EnableFallback { + // Set fallback fallbackConfigs, err := buildTrojanFallbacks(config.FallBackConfigs) if err == nil { proxySetting = &coreConf.TrojanServerConfig{ @@ -70,6 +74,7 @@ func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag nodeInfo.V2ray.Inbounds[0].StreamSetting = &coreConf.StreamConfig{Network: &t} } else if nodeInfo.NodeType == "Shadowsocks" { defer func() { + //Clear v2ray config nodeInfo.V2ray = nil }() nodeInfo.V2ray = &panel.V2rayConfig{} @@ -94,18 +99,7 @@ func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag nodeInfo.V2ray.Inbounds[0].StreamSetting = &coreConf.StreamConfig{Network: &t} } else { return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks", nodeInfo.NodeType) - } /*else if nodeInfo.NodeType == "dokodemo-door" { - nodeInfo.V2ray = &api.V2rayConfig{} - nodeInfo.V2ray.Inbounds = make([]coreConf.InboundDetourConfig, 1) - nodeInfo.V2ray.Inbounds[0].Protocol = "dokodemo-door" - proxySetting = struct { - Host string `json:"address"` - NetworkList []string `json:"network"` - }{ - Host: "v1.mux.cool", - NetworkList: []string{"tcp", "udp"}, - } - }*/ + } // Build Listen IP address ipAddress := net.ParseAddress(config.ListenIP) nodeInfo.V2ray.Inbounds[0].ListenOn = &coreConf.Address{Address: ipAddress} @@ -122,7 +116,6 @@ func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag var setting json.RawMessage // Build Protocol and Protocol setting - setting, err := json.Marshal(proxySetting) if err != nil { return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err) @@ -133,12 +126,12 @@ func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag } else { tcpSetting := &coreConf.TCPConfig{ AcceptProxyProtocol: config.EnableProxyProtocol, - } + } //Enable proxy protocol nodeInfo.V2ray.Inbounds[0].StreamSetting.TCPSettings = tcpSetting } } else if *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network == "ws" { nodeInfo.V2ray.Inbounds[0].StreamSetting.WSSettings = &coreConf.WebSocketConfig{ - AcceptProxyProtocol: config.EnableProxyProtocol} + AcceptProxyProtocol: config.EnableProxyProtocol} //Enable proxy protocol } // Build TLS and XTLS settings if nodeInfo.EnableTls && config.CertConfig.CertMode != "none" { @@ -152,7 +145,6 @@ func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag RejectUnknownSNI: config.CertConfig.RejectUnknownSni, } tlsSettings.Certs = append(tlsSettings.Certs, &coreConf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600}) - nodeInfo.V2ray.Inbounds[0].StreamSetting.TLSSettings = tlsSettings } else if nodeInfo.TLSType == "xtls" { xtlsSettings := &coreConf.XTLSConfig{ @@ -171,7 +163,7 @@ func InboundBuilder(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag config.EnableProxyProtocol { sockoptConfig := &coreConf.SocketConfig{ AcceptProxyProtocol: config.EnableProxyProtocol, - } + } //Enable proxy protocol nodeInfo.V2ray.Inbounds[0].StreamSetting.SocketSettings = sockoptConfig } nodeInfo.V2ray.Inbounds[0].Settings = &setting diff --git a/node/inboundbuilder_test.go b/node/inbound_test.go similarity index 94% rename from node/inboundbuilder_test.go rename to node/inbound_test.go index e114b7c..c80dd0a 100644 --- a/node/inboundbuilder_test.go +++ b/node/inbound_test.go @@ -28,7 +28,7 @@ func TestBuildV2ray(t *testing.T) { config := &Config{ CertConfig: certConfig, } - _, err := InboundBuilder(config, nodeInfo) + _, err := buildInbound(config, nodeInfo) if err != nil { t.Error(err) } @@ -60,7 +60,7 @@ func TestBuildTrojan(t *testing.T) { config := &Config{ CertConfig: certConfig, } - _, err := InboundBuilder(config, nodeInfo) + _, err := buildInbound(config, nodeInfo) if err != nil { t.Error(err) } @@ -92,7 +92,7 @@ func TestBuildSS(t *testing.T) { config := &Config{ CertConfig: certConfig, } - _, err := InboundBuilder(config, nodeInfo) + _, err := buildInbound(config, nodeInfo) if err != nil { t.Error(err) } diff --git a/node/node.go b/node/node.go index 651c30b..ae120bc 100644 --- a/node/node.go +++ b/node/node.go @@ -1,13 +1,13 @@ package node import ( + "errors" "fmt" "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" "github.com/Yuzuki616/V2bX/core" "github.com/xtls/xray-core/common/task" "log" - "runtime" "time" ) @@ -41,27 +41,29 @@ func (c *Node) Start() error { // First fetch Node Info newNodeInfo, err := c.apiClient.GetNodeInfo() if err != nil { - return err + return fmt.Errorf("get node info failed: %s", err) } c.nodeInfo = newNodeInfo c.Tag = c.buildNodeTag() // Add new tag err = c.addNewTag(newNodeInfo) if err != nil { - log.Panic(err) - return err + return fmt.Errorf("add new tag failed: %s", err) } // Update user c.userList, err = c.apiClient.GetUserList() if err != nil { - return err + return fmt.Errorf("get user list failed: %s", err) + } + if len(c.userList) == 0 { + return errors.New("add users failed: not have any user") } err = c.addNewUser(c.userList, newNodeInfo) if err != nil { return err } if err := c.server.AddInboundLimiter(c.Tag, c.nodeInfo); err != nil { - log.Print(err) + return fmt.Errorf("add inbound limiter failed: %s", err) } // Add Rule Manager if !c.config.DisableGetRule { @@ -69,17 +71,19 @@ func (c *Node) Start() error { log.Printf("Get rule list filed: %s", err) } else if ruleList != nil { if err := c.server.UpdateRule(c.Tag, ruleList); err != nil { - log.Print(err) + log.Printf("Update rule filed: %s", err) } } } + // fetch node info task c.nodeInfoMonitorPeriodic = &task.Periodic{ Interval: time.Duration(c.config.UpdatePeriodic) * time.Second, Execute: c.nodeInfoMonitor, } + // fetch user list task c.userReportPeriodic = &task.Periodic{ Interval: time.Duration(c.config.UpdatePeriodic) * time.Second, - Execute: c.userInfoMonitor, + Execute: c.reportUserTraffic, } log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId) // delay to start nodeInfoMonitor @@ -95,9 +99,10 @@ func (c *Node) Start() error { _ = c.userReportPeriodic.Start() }() if c.config.EnableIpRecorder { + // report and fetch online ip list task c.onlineIpReportPeriodic = &task.Periodic{ Interval: time.Duration(c.config.IpRecorderConfig.Periodic) * time.Second, - Execute: c.onlineIpReport, + Execute: c.reportOnlineIp, } go func() { time.Sleep(time.Duration(c.config.IpRecorderConfig.Periodic) * time.Second) @@ -106,9 +111,10 @@ func (c *Node) Start() error { log.Printf("[%s: %d] Start report online ip", c.nodeInfo.NodeType, c.nodeInfo.NodeId) } if c.config.EnableDynamicSpeedLimit { + // Check dynamic speed limit task c.DynamicSpeedLimitPeriodic = &task.Periodic{ Interval: time.Duration(c.config.DynamicSpeedLimitConfig.Periodic) * time.Second, - Execute: c.DynamicSpeedLimit, + Execute: c.dynamicSpeedLimit, } go func() { time.Sleep(time.Duration(c.config.DynamicSpeedLimitConfig.Periodic) * time.Second) @@ -116,7 +122,6 @@ func (c *Node) Start() error { }() log.Printf("[%s: %d] Start dynamic speed limit", c.nodeInfo.NodeType, c.nodeInfo.NodeId) } - runtime.GC() return nil } @@ -141,6 +146,12 @@ func (c *Node) Close() error { log.Panicf("online ip report periodic close failed: %s", err) } } + if c.DynamicSpeedLimitPeriodic != nil { + err := c.DynamicSpeedLimitPeriodic.Close() + if err != nil { + log.Panicf("dynamic speed limit periodic close failed: %s", err) + } + } return nil } diff --git a/node/outboundbuilder.go b/node/outbound.go similarity index 73% rename from node/outboundbuilder.go rename to node/outbound.go index 27c25b6..abbb7f5 100644 --- a/node/outboundbuilder.go +++ b/node/outbound.go @@ -11,8 +11,8 @@ import ( "github.com/xtls/xray-core/infra/conf" ) -//OutboundBuilder build freedom outbund config for addoutbound -func OutboundBuilder(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.OutboundHandlerConfig, error) { +// buildOutbound build freedom outbund config for addoutbound +func buildOutbound(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.OutboundHandlerConfig, error) { outboundDetourConfig := &conf.OutboundDetourConfig{} outboundDetourConfig.Protocol = "freedom" outboundDetourConfig.Tag = tag @@ -35,10 +35,6 @@ func OutboundBuilder(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, t proxySetting := &conf.FreedomConfig{ DomainStrategy: domainStrategy, } - // Used for Shadowsocks-Plugin - if nodeInfo.NodeType == "dokodemo-door" { - proxySetting.Redirect = fmt.Sprintf("127.0.0.1:%d", nodeInfo.V2ray.Inbounds[0].PortList.Range[0].From-1) - } var setting json.RawMessage setting, err := json.Marshal(proxySetting) if err != nil { diff --git a/node/task.go b/node/task.go index 5ca0de8..b4ab3d6 100644 --- a/node/task.go +++ b/node/task.go @@ -9,7 +9,6 @@ import ( "github.com/goccy/go-json" "github.com/xtls/xray-core/common/protocol" "log" - "math" "reflect" "runtime" "time" @@ -118,6 +117,7 @@ func (c *Node) nodeInfoMonitor() (err error) { } } if len(added) > 0 || len(deleted) > 0 { + defer runtime.GC() // Update Limiter if err := c.server.UpdateInboundLimiter(c.Tag, deleted); err != nil { log.Print(err) @@ -127,7 +127,6 @@ func (c *Node) nodeInfoMonitor() (err error) { len(deleted), len(added)) c.userList = newUserInfo newUserInfo = nil - runtime.GC() } return nil } @@ -145,7 +144,7 @@ func (c *Node) removeOldTag(oldTag string) (err error) { } func (c *Node) addNewTag(newNodeInfo *panel.NodeInfo) (err error) { - inboundConfig, err := InboundBuilder(c.config, newNodeInfo, c.Tag) + inboundConfig, err := buildInbound(c.config, newNodeInfo, c.Tag) if err != nil { return err } @@ -154,7 +153,7 @@ func (c *Node) addNewTag(newNodeInfo *panel.NodeInfo) (err error) { return err } - outBoundConfig, err := OutboundBuilder(c.config, newNodeInfo, c.Tag) + outBoundConfig, err := buildOutbound(c.config, newNodeInfo, c.Tag) if err != nil { return err @@ -173,14 +172,7 @@ func (c *Node) addNewUser(userInfo []panel.UserInfo, nodeInfo *panel.NodeInfo) ( if nodeInfo.EnableVless { users = c.buildVlessUsers(userInfo) } else { - alterID := 0 - alterID = (userInfo)[0].V2rayUser.AlterId - if alterID >= 0 && alterID < math.MaxUint16 { - users = c.buildVmessUsers(userInfo, uint16(alterID)) - } else { - users = c.buildVmessUsers(userInfo, 0) - return fmt.Errorf("AlterID should between 0 to 1<<16 - 1, set it to 0 for now") - } + users = c.buildVmessUsers(userInfo) } } else if nodeInfo.NodeType == "Trojan" { users = c.buildTrojanUsers(userInfo) @@ -225,7 +217,7 @@ func compareUserList(old, new []panel.UserInfo) (deleted, added []panel.UserInfo return deleted, added } -func (c *Node) userInfoMonitor() (err error) { +func (c *Node) reportUserTraffic() (err error) { // Get User traffic userTraffic := make([]panel.UserTraffic, 0) for i := range c.userList { @@ -243,7 +235,7 @@ func (c *Node) userInfoMonitor() (err error) { if len(userTraffic) > 0 && !c.config.DisableUploadTraffic { err = c.apiClient.ReportUserTraffic(userTraffic) if err != nil { - log.Print(err) + log.Printf("Report user traffic faild: %s", err) } else { log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(userTraffic)) } @@ -256,7 +248,7 @@ func (c *Node) userInfoMonitor() (err error) { return nil } -func (c *Node) onlineIpReport() (err error) { +func (c *Node) reportOnlineIp() (err error) { onlineIp, err := c.server.ListOnlineIp(c.Tag) if err != nil { log.Print(err) @@ -292,7 +284,7 @@ func (c *Node) onlineIpReport() (err error) { return nil } -func (c *Node) DynamicSpeedLimit() error { +func (c *Node) dynamicSpeedLimit() error { if c.config.EnableDynamicSpeedLimit { for i := range c.userList { up, down := c.server.GetUserTraffic(c.buildUserTag(&(c.userList)[i]), false) diff --git a/node/userbuilder.go b/node/user.go similarity index 90% rename from node/userbuilder.go rename to node/user.go index 2475f05..7c0713c 100644 --- a/node/userbuilder.go +++ b/node/user.go @@ -13,10 +13,10 @@ import ( "github.com/xtls/xray-core/proxy/vless" ) -func (c *Node) buildVmessUsers(userInfo []panel.UserInfo, serverAlterID uint16) (users []*protocol.User) { +func (c *Node) buildVmessUsers(userInfo []panel.UserInfo) (users []*protocol.User) { users = make([]*protocol.User, len(userInfo)) for i, user := range userInfo { - users[i] = c.buildVmessUser(&user, serverAlterID) + users[i] = c.buildVmessUser(&user, 0) } return users } @@ -27,12 +27,11 @@ func (c *Node) buildVmessUser(userInfo *panel.UserInfo, serverAlterID uint16) (u AlterIds: serverAlterID, Security: "auto", } - user = &protocol.User{ + return &protocol.User{ Level: 0, Email: c.buildUserTag(userInfo), // Uid: InboundTag|email|uid Account: serial.ToTypedMessage(vmessAccount.Build()), } - return user } func (c *Node) buildVlessUsers(userInfo []panel.UserInfo) (users []*protocol.User) { @@ -48,12 +47,11 @@ func (c *Node) buildVlessUser(userInfo *panel.UserInfo) (user *protocol.User) { Id: userInfo.V2rayUser.Uuid, Flow: "xtls-rprx-direct", } - user = &protocol.User{ + return &protocol.User{ Level: 0, Email: c.buildUserTag(userInfo), Account: serial.ToTypedMessage(vlessAccount), } - return user } func (c *Node) buildTrojanUsers(userInfo []panel.UserInfo) (users []*protocol.User) { @@ -69,12 +67,11 @@ func (c *Node) buildTrojanUser(userInfo *panel.UserInfo) (user *protocol.User) { Password: userInfo.TrojanUser.Password, Flow: "xtls-rprx-direct", } - user = &protocol.User{ + return &protocol.User{ Level: 0, Email: c.buildUserTag(userInfo), Account: serial.ToTypedMessage(trojanAccount), } - return user } func getCipherFromString(c string) shadowsocks.CipherType { @@ -105,12 +102,11 @@ func (c *Node) buildSSUser(userInfo *panel.UserInfo, cypher shadowsocks.CipherTy Password: userInfo.Secret, CipherType: cypher, } - user = &protocol.User{ + return &protocol.User{ Level: 0, Email: c.buildUserTag(userInfo), Account: serial.ToTypedMessage(ssAccount), } - return user } func (c *Node) buildUserTag(user *panel.UserInfo) string {