change project structure

add across nodes ip limit
add user ip recorder
del config file watch
This commit is contained in:
yuzuki999 committed 2022-07-28 23:00:05 +08:00
1 parent c702ea1461
commit efcdffb20d
43 files changed
+877 -893

No files matched your search

+10 -10
View File
@@ -1,16 +1,16 @@
main/config.yml example/config.yml
main/main example/main
main/XrayR example/XrayR
main/XrayR* example/XrayR*
main/mytest example/mytest
main/access.logo example/access.logo
main/error.log example/error.log
api/chooseparser.go.bak api/chooseparser.go.bak
common/Inboundbuilder/.lego/ common/Inboundbuilder/.lego/
common/legocmd/.lego/ common/legocmd/.lego/
.vscode/launch.json .vscode/launch.json
main/.lego example/.lego
main/cert example/cert
main/config.yml example/config.yml
./vscode ./vscode
.idea/* .idea/*
+2 -17
View File
@@ -4,6 +4,7 @@
package api package api
import ( import (
"github.com/Yuzuki616/V2bX/conf"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
"log" "log"
"strconv" "strconv"
@@ -13,21 +14,6 @@ import (
// API is the interface for different panel's api. // API is the interface for different panel's api.
type Config struct {
APIHost string `mapstructure:"ApiHost"`
NodeID int `mapstructure:"NodeID"`
Key string `mapstructure:"ApiKey"`
NodeType string `mapstructure:"NodeType"`
EnableVless bool `mapstructure:"EnableVless"`
EnableXTLS bool `mapstructure:"EnableXTLS"`
//EnableSS2022 bool `mapstructure:"EnableSS2022"`
Timeout int `mapstructure:"Timeout"`
SpeedLimit float64 `mapstructure:"SpeedLimit"`
DeviceLimit int `mapstructure:"DeviceLimit"`
RuleListPath string `mapstructure:"RuleListPath"`
DisableCustomConfig bool `mapstructure:"DisableCustomConfig"`
}
type ClientInfo struct { type ClientInfo struct {
APIHost string APIHost string
NodeID int NodeID int
@@ -53,8 +39,7 @@ type Client struct {
NodeRuleRspMd5 [16]byte NodeRuleRspMd5 [16]byte
} }
func New(apiConfig *Config) API { func New(apiConfig *conf.ApiConfig) API {
client := resty.New() client := resty.New()
client.SetRetryCount(3) client.SetRetryCount(3)
if apiConfig.Timeout > 0 { if apiConfig.Timeout > 0 {
@@ -4,7 +4,7 @@
// protoc v3.19.4 // protoc v3.19.4
// source: app/mydispatcher/config.proto // source: app/mydispatcher/config.proto
package mydispatcher package dispatcher
import ( import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
File renamed without changes.
@@ -1,4 +1,4 @@
package mydispatcher package dispatcher
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
@@ -1,4 +1,4 @@
// Package dispather implement the rate limiter and the onlie device counter // Package dispather implement the rate limiter and the onlie device counter
package mydispatcher package dispatcher
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
@@ -1,4 +1,4 @@
package mydispatcher package dispatcher
import "github.com/xtls/xray-core/common/errors" import "github.com/xtls/xray-core/common/errors"
@@ -1,4 +1,4 @@
package mydispatcher package dispatcher
import ( import (
"context" "context"
@@ -1,4 +1,4 @@
package mydispatcher package dispatcher
import ( import (
"context" "context"
@@ -1,4 +1,4 @@
package mydispatcher package dispatcher
import ( import (
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
@@ -1,4 +1,4 @@
package mydispatcher_test package dispatcher_test
import ( import (
"testing" "testing"
+74 -34
View File
@@ -19,9 +19,9 @@ type UserInfo struct {
type InboundInfo struct { type InboundInfo struct {
Tag string Tag string
NodeSpeedLimit uint64 NodeSpeedLimit uint64
UserInfo *sync.Map // Key: Email value: UserInfo UserInfo *sync.Map // Key: Uid value: UserInfo
BucketHub *sync.Map // key: Email, value: *ratelimit.Bucket BucketHub *sync.Map // key: Uid, value: *ratelimit.Bucket
UserOnlineIP *sync.Map // Key: Email Value: *sync.Map: Key: IP, Value: UID UserOnlineIP *sync.Map // Key: Uid Value: *sync.Map: Key: IP, Value: bool
} }
type Limiter struct { type Limiter struct {
@@ -49,7 +49,7 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeInfo *api.NodeInfo, userList
if (*userList)[i].DeviceLimit == 0 { if (*userList)[i].DeviceLimit == 0 {
(*userList)[i].DeviceLimit = nodeInfo.DeviceLimit (*userList)[i].DeviceLimit = nodeInfo.DeviceLimit
}*/ }*/
userMap.Store(fmt.Sprintf("%s|%s|%d", tag, (*userList)[i].GetUserEmail(), (*userList)[i].UID), UserInfo{ userMap.Store(fmt.Sprintf("%s|%s|%d", tag, (*userList)[i].V2rayUser.Email, (*userList)[i].UID), UserInfo{
UID: (*userList)[i].UID, UID: (*userList)[i].UID,
SpeedLimit: nodeInfo.SpeedLimit, SpeedLimit: nodeInfo.SpeedLimit,
DeviceLimit: nodeInfo.DeviceLimit, DeviceLimit: nodeInfo.DeviceLimit,
@@ -60,24 +60,19 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeInfo *api.NodeInfo, userList
return nil return nil
} }
func (l *Limiter) UpdateInboundLimiter(tag string, nodeInfo *api.NodeInfo, updatedUserList *[]api.UserInfo, usersIndex *[]int) error { func (l *Limiter) UpdateInboundLimiter(tag string, nodeInfo *api.NodeInfo, updatedUserList *[]api.UserInfo) error {
if value, ok := l.InboundInfo.Load(tag); ok { if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo) inboundInfo := value.(*InboundInfo)
// Update User info // Update User info
for _, u := range *usersIndex { for i := range *updatedUserList {
/*if (*updatedUserList)[u].SpeedLimit == 0 { inboundInfo.UserInfo.Store(fmt.Sprintf("%s|%s|%d", tag,
(*updatedUserList)[u].SpeedLimit = nodeInfo.SpeedLimit (*updatedUserList)[i].V2rayUser.Email, (*updatedUserList)[i].UID), UserInfo{
} UID: (*updatedUserList)[i].UID,
if (*updatedUserList)[u].DeviceLimit == 0 {
(*updatedUserList)[u].DeviceLimit = nodeInfo.DeviceLimit
}*/
inboundInfo.UserInfo.Store(fmt.Sprintf("%s|%s|%d", tag, (*updatedUserList)[u].GetUserEmail(), (*updatedUserList)[u].UID), UserInfo{
UID: (*updatedUserList)[u].UID,
SpeedLimit: nodeInfo.SpeedLimit, SpeedLimit: nodeInfo.SpeedLimit,
DeviceLimit: nodeInfo.DeviceLimit, DeviceLimit: nodeInfo.DeviceLimit,
}) })
inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag, (*updatedUserList)[u].GetUserEmail(), (*updatedUserList)[u].UID)) // Delete old limiter bucket inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag,
(*updatedUserList)[i].V2rayUser.Email, (*updatedUserList)[i].UID)) // Delete old limiter bucket
} }
} else { } else {
return fmt.Errorf("no such inbound in limiter: %s", tag) return fmt.Errorf("no such inbound in limiter: %s", tag)
@@ -90,34 +85,78 @@ func (l *Limiter) DeleteInboundLimiter(tag string) error {
return nil return nil
} }
func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) { type UserIp struct {
onlineUser := make([]api.OnlineUser, 0) Uid int `json:"Uid"`
IPs []string `json:"Ips"`
}
func (l *Limiter) GetOnlineUserIp(tag string) (*[]UserIp, error) {
if value, ok := l.InboundInfo.Load(tag); ok { if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo) inboundInfo := value.(*InboundInfo)
// Clear Speed Limiter bucket for users who are not online // Clear Speed Limiter bucket for users who are not online
inboundInfo.BucketHub.Range(func(key, value interface{}) bool { inboundInfo.BucketHub.Range(func(key, value interface{}) bool {
email := key.(string) if _, exists := inboundInfo.UserOnlineIP.Load(key.(string)); !exists {
if _, exists := inboundInfo.UserOnlineIP.Load(email); !exists { inboundInfo.BucketHub.Delete(key.(string))
inboundInfo.BucketHub.Delete(email)
} }
return true return true
}) })
onlineUser := make([]UserIp, 0)
var ipMap *sync.Map
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool { inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
ipMap := value.(*sync.Map) ipMap = value.(*sync.Map)
ipMap.Range(func(key, value interface{}) bool { var ip []string
ip := key.(string) ipMap.Range(func(key, v interface{}) bool {
uid := value.(int) if v.(bool) {
onlineUser = append(onlineUser, api.OnlineUser{UID: uid, IP: ip}) ip = append(ip, key.(string))
}
return true return true
}) })
email := key.(string) if len(ip) > 0 {
inboundInfo.UserOnlineIP.Delete(email) // Reset online device if u, ok := inboundInfo.UserInfo.Load(key.(string)); ok {
onlineUser = append(onlineUser, UserIp{
Uid: u.(UserInfo).UID,
IPs: ip,
})
}
}
return true return true
}) })
if len(onlineUser) == 0 {
return nil, nil
}
return &onlineUser, nil
} else { } else {
return nil, fmt.Errorf("no such inbound in limiter: %s", tag) return nil, fmt.Errorf("no such inbound in limiter: %s", tag)
} }
return &onlineUser, nil }
func (l *Limiter) UpdateOnlineUserIP(tag string, userIpList *[]UserIp) {
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
for i := range *userIpList {
ipMap := new(sync.Map)
for _, userIp := range (*userIpList)[i].IPs {
ipMap.Store(userIp, false)
}
inboundInfo.UserOnlineIP.Store((*userIpList)[i].Uid, ipMap)
}
}
}
func (l *Limiter) ClearOnlineUserIP(tag string) {
if v, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := v.(*InboundInfo)
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
inboundInfo.UserOnlineIP.Delete(key)
return true
})
}
} }
func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *ratelimit.Bucket, SpeedLimit bool, Reject bool) { func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *ratelimit.Bucket, SpeedLimit bool, Reject bool) {
@@ -126,21 +165,18 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
nodeLimit := inboundInfo.NodeSpeedLimit nodeLimit := inboundInfo.NodeSpeedLimit
var userLimit uint64 = 0 var userLimit uint64 = 0
var deviceLimit = 0 var deviceLimit = 0
var uid = 0
if v, ok := inboundInfo.UserInfo.Load(email); ok { if v, ok := inboundInfo.UserInfo.Load(email); ok {
u := v.(UserInfo) u := v.(UserInfo)
uid = u.UID
userLimit = u.SpeedLimit userLimit = u.SpeedLimit
deviceLimit = u.DeviceLimit deviceLimit = u.DeviceLimit
} }
// Report online device
ipMap := new(sync.Map) ipMap := new(sync.Map)
ipMap.Store(ip, uid) ipMap.Store(ip, true)
// If any device is online // If any device is online
if v, ok := inboundInfo.UserOnlineIP.LoadOrStore(email, ipMap); ok { if v, ok := inboundInfo.UserOnlineIP.LoadOrStore(email, ipMap); ok {
ipMap := v.(*sync.Map) ipMap := v.(*sync.Map)
// If this ip is a new device // If this ip is a new device
if _, ok := ipMap.LoadOrStore(ip, uid); !ok { if online, ok := ipMap.LoadOrStore(ip, true); !ok {
counter := 0 counter := 0
ipMap.Range(func(key, value interface{}) bool { ipMap.Range(func(key, value interface{}) bool {
counter++ counter++
@@ -150,6 +186,10 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
ipMap.Delete(ip) ipMap.Delete(ip)
return nil, false, true return nil, false, true
} }
} else {
if !online.(bool) {
ipMap.Store(ip, true)
}
} }
} }
limit := determineRate(nodeLimit, userLimit) // If need the Speed limit limit := determineRate(nodeLimit, userLimit) // If need the Speed limit
+23
View File
@@ -0,0 +1,23 @@
package conf
type Conf struct {
LogConfig *LogConfig `mapstructure:"Log"`
DnsConfigPath string `mapstructure:"DnsConfigPath"`
InboundConfigPath string `mapstructure:"InboundConfigPath"`
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
RouteConfigPath string `mapstructure:"RouteConfigPath"`
ConnectionConfig *ConnetionConfig `mapstructure:"ConnectionConfig"`
NodesConfig []*NodeConfig `mapstructure:"Nodes"`
}
func New() *Conf {
return &Conf{
LogConfig: NewLogConfig(),
DnsConfigPath: "",
InboundConfigPath: "",
OutboundConfigPath: "",
RouteConfigPath: "",
ConnectionConfig: NewConnetionConfig(),
NodesConfig: []*NodeConfig{},
}
}
+19
View File
@@ -0,0 +1,19 @@
package conf
type ConnetionConfig struct {
Handshake uint32 `mapstructure:"handshake"`
ConnIdle uint32 `mapstructure:"connIdle"`
UplinkOnly uint32 `mapstructure:"uplinkOnly"`
DownlinkOnly uint32 `mapstructure:"downlinkOnly"`
BufferSize int32 `mapstructure:"bufferSize"`
}
func NewConnetionConfig() *ConnetionConfig {
return &ConnetionConfig{
Handshake: 4,
ConnIdle: 30,
UplinkOnly: 2,
DownlinkOnly: 4,
BufferSize: 64,
}
}
+15
View File
@@ -0,0 +1,15 @@
package conf
type LogConfig struct {
Level string `mapstructure:"Level"`
AccessPath string `mapstructure:"AccessPath"`
ErrorPath string `mapstructure:"ErrorPath"`
}
func NewLogConfig() *LogConfig {
return &LogConfig{
Level: "none",
AccessPath: "",
ErrorPath: "",
}
}
+47 -17
View File
@@ -1,20 +1,4 @@
package controller package conf
type Config struct {
ListenIP string `mapstructure:"ListenIP"`
SendIP string `mapstructure:"SendIP"`
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
CertConfig *CertConfig `mapstructure:"CertConfig"`
EnableDNS bool `mapstructure:"EnableDNS"`
DNSType string `mapstructure:"DNSType"`
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
DisableGetRule bool `mapstructure:"DisableGetRule"`
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
EnableFallback bool `mapstructure:"EnableFallback"`
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
DisableSniffing bool `mapstructure:"DisableSniffing"`
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
}
type CertConfig struct { type CertConfig struct {
CertMode string `mapstructure:"CertMode"` // none, file, http, dns CertMode string `mapstructure:"CertMode"` // none, file, http, dns
@@ -34,3 +18,49 @@ type FallBackConfig struct {
Dest string `mapstructure:"Dest"` Dest string `mapstructure:"Dest"`
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"` ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
} }
type IpReportConfig struct {
Url string `mapstructure:"Url"`
Token string `mapstructure:"Token"`
Periodic int `mapstructure:"Periodic"`
Timeout int `mapstructure:"Timeout"`
EnableIpSync bool `mapstructure:"EnableIpSync"`
}
type ControllerConfig struct {
ListenIP string `mapstructure:"ListenIP"`
SendIP string `mapstructure:"SendIP"`
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
EnableDNS bool `mapstructure:"EnableDNS"`
DNSType string `mapstructure:"DNSType"`
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
DisableGetRule bool `mapstructure:"DisableGetRule"`
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
EnableFallback bool `mapstructure:"EnableFallback"`
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
DisableSniffing bool `mapstructure:"DisableSniffing"`
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
EnableIpRecorder bool `mapstructure:"EnableIpRecorder"`
IpRecorderConfig *IpReportConfig `mapstructure:"IpRecorderConfig"`
CertConfig *CertConfig `mapstructure:"CertConfig"`
}
type ApiConfig struct {
APIHost string `mapstructure:"ApiHost"`
NodeID int `mapstructure:"NodeID"`
Key string `mapstructure:"ApiKey"`
NodeType string `mapstructure:"NodeType"`
EnableVless bool `mapstructure:"EnableVless"`
EnableXTLS bool `mapstructure:"EnableXTLS"`
//EnableSS2022 bool `mapstructure:"EnableSS2022"`
Timeout int `mapstructure:"Timeout"`
SpeedLimit float64 `mapstructure:"SpeedLimit"`
DeviceLimit int `mapstructure:"DeviceLimit"`
RuleListPath string `mapstructure:"RuleListPath"`
DisableCustomConfig bool `mapstructure:"DisableCustomConfig"`
}
type NodeConfig struct {
ApiConfig *ApiConfig `mapstructure:"ApiConfig"`
ControllerConfig *ControllerConfig `mapstructure:"ControllerConfig"`
}
@@ -40,6 +40,13 @@ Nodes:
Path: # HTTP PATH, Empty for any Path: # HTTP PATH, Empty for any
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details. Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
EnableIpRecorder: false # Enable online ip report
IpRecorderConfig:
Url: "http://127.0.0.1:123" # Report url
Token: "123" # Report token
Periodic: 60 # Report interval, sec.
Timeout: 10 # Report timeout, sec.
EnableIpSync: false # Enable online ip sync
CertConfig: CertConfig:
CertMode: dns # Option about how to get certificate: none, file, http, dns. Choose "none" will forcedly disable the tls config. CertMode: dns # Option about how to get certificate: none, file, http, dns. Choose "none" will forcedly disable the tls config.
CertDomain: "node1.test.com" # Domain to cert CertDomain: "node1.test.com" # Domain to cert
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
+30 -33
View File
@@ -3,6 +3,11 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/api"
"github.com/Yuzuki616/V2bX/conf"
"github.com/Yuzuki616/V2bX/node"
"github.com/Yuzuki616/V2bX/xray"
"github.com/spf13/viper"
"log" "log"
"os" "os"
"os/signal" "os/signal"
@@ -10,11 +15,6 @@ import (
"runtime" "runtime"
"strings" "strings"
"syscall" "syscall"
"time"
"github.com/Yuzuki616/V2bX/panel"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
) )
var ( var (
@@ -23,7 +23,7 @@ var (
) )
var ( var (
version = "v0.0.2" version = "v0.0.4"
codename = "V2bX" codename = "V2bX"
intro = "A V2board backend based on Xray" intro = "A V2board backend based on Xray"
) )
@@ -34,7 +34,6 @@ func showVersion() {
func getConfig() *viper.Viper { func getConfig() *viper.Viper {
config := viper.New() config := viper.New()
// Set custom path and name // Set custom path and name
if *configFile != "" { if *configFile != "" {
configName := path.Base(*configFile) configName := path.Base(*configFile)
@@ -52,46 +51,44 @@ func getConfig() *viper.Viper {
config.SetConfigName("config") config.SetConfigName("config")
config.SetConfigType("yml") config.SetConfigType("yml")
config.AddConfigPath(".") config.AddConfigPath(".")
} }
if err := config.ReadInConfig(); err != nil { if err := config.ReadInConfig(); err != nil {
log.Panicf("Fatal error config file: %s \n", err) log.Panicf("Fatal error config file: %s \n", err)
} }
config.WatchConfig() // Watch the config
return config return config
} }
func startNodes(nodes []*conf.NodeConfig, core *xray.Xray) error {
for i, _ := range nodes {
var apiClient = api.New(nodes[i].ApiConfig)
// 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 nil
}
func main() { func main() {
flag.Parse() flag.Parse()
showVersion() showVersion()
if *printVersion { if *printVersion {
return return
} }
config := getConfig() config := getConfig()
panelConfig := &panel.Config{} c := conf.New()
config.Unmarshal(panelConfig) err := config.Unmarshal(c)
p := panel.New(panelConfig) if err != nil {
lastTime := time.Now() log.Panicf("can't unmarshal config file: %s \n", err)
config.OnConfigChange(func(e fsnotify.Event) { }
// Discarding event received within a short period of time after receiving an event. x := xray.New(c)
if time.Now().After(lastTime.Add(3 * time.Second)) { x.Start()
// Hot reload function defer x.Close()
fmt.Println("Config file changed:", e.Name) err = startNodes(c.NodesConfig, x)
p.Close() if err != nil {
// Delete old instance and trigger GC log.Panicf("run nodes error: %v", err)
runtime.GC() }
config.Unmarshal(panelConfig)
p.Start()
lastTime = time.Now()
}
})
p.Start()
defer p.Close()
//Explicitly triggering GC to remove garbage from config loading. //Explicitly triggering GC to remove garbage from config loading.
runtime.GC() runtime.GC()
// Running backend // Running backend
@@ -1,19 +1,20 @@
// Package controller the InbounderConfig used by add inbound // Package node the InbounderConfig used by add inbound
package controller package node
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/api" "github.com/Yuzuki616/V2bX/api"
"github.com/Yuzuki616/V2bX/common/legoCmd" "github.com/Yuzuki616/V2bX/common/legoCmd"
"github.com/Yuzuki616/V2bX/conf"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf" coreConf "github.com/xtls/xray-core/infra/conf"
) )
//InboundBuilder build Inbound config for different protocol //InboundBuilder build Inbound config for different protocol
func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.InboundHandlerConfig, error) { func InboundBuilder(config *conf.ControllerConfig, nodeInfo *api.NodeInfo, tag string) (*core.InboundHandlerConfig, error) {
var proxySetting interface{} var proxySetting interface{}
if nodeInfo.NodeType == "V2ray" { if nodeInfo.NodeType == "V2ray" {
defer func() { defer func() {
@@ -25,7 +26,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
if config.EnableFallback { if config.EnableFallback {
fallbackConfigs, err := buildVlessFallbacks(config.FallBackConfigs) fallbackConfigs, err := buildVlessFallbacks(config.FallBackConfigs)
if err == nil { if err == nil {
proxySetting = &conf.VLessInboundConfig{ proxySetting = &coreConf.VLessInboundConfig{
Decryption: "none", Decryption: "none",
Fallbacks: fallbackConfigs, Fallbacks: fallbackConfigs,
} }
@@ -33,13 +34,13 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
return nil, err return nil, err
} }
} else { } else {
proxySetting = &conf.VLessInboundConfig{ proxySetting = &coreConf.VLessInboundConfig{
Decryption: "none", Decryption: "none",
} }
} }
} else { } else {
nodeInfo.V2ray.Inbounds[0].Protocol = "vmess" nodeInfo.V2ray.Inbounds[0].Protocol = "vmess"
proxySetting = &conf.VMessInboundConfig{} proxySetting = &coreConf.VMessInboundConfig{}
} }
} else if nodeInfo.NodeType == "Trojan" { } else if nodeInfo.NodeType == "Trojan" {
defer func() { defer func() {
@@ -47,55 +48,55 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
nodeInfo.Trojan = nil nodeInfo.Trojan = nil
}() }()
nodeInfo.V2ray = &api.V2rayConfig{} nodeInfo.V2ray = &api.V2rayConfig{}
nodeInfo.V2ray.Inbounds = make([]conf.InboundDetourConfig, 1) nodeInfo.V2ray.Inbounds = make([]coreConf.InboundDetourConfig, 1)
nodeInfo.V2ray.Inbounds[0].Protocol = "trojan" nodeInfo.V2ray.Inbounds[0].Protocol = "trojan"
// Enable fallback // Enable fallback
if config.EnableFallback { if config.EnableFallback {
fallbackConfigs, err := buildTrojanFallbacks(config.FallBackConfigs) fallbackConfigs, err := buildTrojanFallbacks(config.FallBackConfigs)
if err == nil { if err == nil {
proxySetting = &conf.TrojanServerConfig{ proxySetting = &coreConf.TrojanServerConfig{
Fallbacks: fallbackConfigs, Fallbacks: fallbackConfigs,
} }
} else { } else {
return nil, err return nil, err
} }
} else { } else {
proxySetting = &conf.TrojanServerConfig{} proxySetting = &coreConf.TrojanServerConfig{}
} }
nodeInfo.V2ray.Inbounds[0].PortList = &conf.PortList{ nodeInfo.V2ray.Inbounds[0].PortList = &coreConf.PortList{
Range: []conf.PortRange{{From: uint32(nodeInfo.Trojan.LocalPort), To: uint32(nodeInfo.Trojan.LocalPort)}}, Range: []coreConf.PortRange{{From: uint32(nodeInfo.Trojan.LocalPort), To: uint32(nodeInfo.Trojan.LocalPort)}},
} }
t := conf.TransportProtocol(nodeInfo.Trojan.TransportProtocol) t := coreConf.TransportProtocol(nodeInfo.Trojan.TransportProtocol)
nodeInfo.V2ray.Inbounds[0].StreamSetting = &conf.StreamConfig{Network: &t} nodeInfo.V2ray.Inbounds[0].StreamSetting = &coreConf.StreamConfig{Network: &t}
} else if nodeInfo.NodeType == "Shadowsocks" { } else if nodeInfo.NodeType == "Shadowsocks" {
defer func() { defer func() {
nodeInfo.V2ray = nil nodeInfo.V2ray = nil
}() }()
nodeInfo.V2ray = &api.V2rayConfig{} nodeInfo.V2ray = &api.V2rayConfig{}
nodeInfo.V2ray.Inbounds = []conf.InboundDetourConfig{{Protocol: "shadowsocks"}} nodeInfo.V2ray.Inbounds = []coreConf.InboundDetourConfig{{Protocol: "shadowsocks"}}
proxySetting = &conf.ShadowsocksServerConfig{} proxySetting = &coreConf.ShadowsocksServerConfig{}
randomPasswd := uuid.New() randomPasswd := uuid.New()
defaultSSuser := &conf.ShadowsocksUserConfig{ defaultSSuser := &coreConf.ShadowsocksUserConfig{
Cipher: "aes-128-gcm", Cipher: "aes-128-gcm",
Password: randomPasswd.String(), Password: randomPasswd.String(),
} }
proxySetting, _ := proxySetting.(*conf.ShadowsocksServerConfig) proxySetting, _ := proxySetting.(*coreConf.ShadowsocksServerConfig)
proxySetting.Users = append(proxySetting.Users, defaultSSuser) proxySetting.Users = append(proxySetting.Users, defaultSSuser)
proxySetting.NetworkList = &conf.NetworkList{"tcp", "udp"} proxySetting.NetworkList = &coreConf.NetworkList{"tcp", "udp"}
proxySetting.IVCheck = true proxySetting.IVCheck = true
if config.DisableIVCheck { if config.DisableIVCheck {
proxySetting.IVCheck = false proxySetting.IVCheck = false
} }
nodeInfo.V2ray.Inbounds[0].PortList = &conf.PortList{ nodeInfo.V2ray.Inbounds[0].PortList = &coreConf.PortList{
Range: []conf.PortRange{{From: uint32(nodeInfo.SS.Port), To: uint32(nodeInfo.SS.Port)}}, Range: []coreConf.PortRange{{From: uint32(nodeInfo.SS.Port), To: uint32(nodeInfo.SS.Port)}},
} }
t := conf.TransportProtocol(nodeInfo.SS.TransportProtocol) t := coreConf.TransportProtocol(nodeInfo.SS.TransportProtocol)
nodeInfo.V2ray.Inbounds[0].StreamSetting = &conf.StreamConfig{Network: &t} nodeInfo.V2ray.Inbounds[0].StreamSetting = &coreConf.StreamConfig{Network: &t}
} else { } else {
return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks", nodeInfo.NodeType) return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks", nodeInfo.NodeType)
} /*else if nodeInfo.NodeType == "dokodemo-door" { } /*else if nodeInfo.NodeType == "dokodemo-door" {
nodeInfo.V2ray = &api.V2rayConfig{} nodeInfo.V2ray = &api.V2rayConfig{}
nodeInfo.V2ray.Inbounds = make([]conf.InboundDetourConfig, 1) nodeInfo.V2ray.Inbounds = make([]coreConf.InboundDetourConfig, 1)
nodeInfo.V2ray.Inbounds[0].Protocol = "dokodemo-door" nodeInfo.V2ray.Inbounds[0].Protocol = "dokodemo-door"
proxySetting = struct { proxySetting = struct {
Host string `json:"address"` Host string `json:"address"`
@@ -107,11 +108,11 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
}*/ }*/
// Build Listen IP address // Build Listen IP address
ipAddress := net.ParseAddress(config.ListenIP) ipAddress := net.ParseAddress(config.ListenIP)
nodeInfo.V2ray.Inbounds[0].ListenOn = &conf.Address{Address: ipAddress} nodeInfo.V2ray.Inbounds[0].ListenOn = &coreConf.Address{Address: ipAddress}
// SniffingConfig // SniffingConfig
sniffingConfig := &conf.SniffingConfig{ sniffingConfig := &coreConf.SniffingConfig{
Enabled: true, Enabled: true,
DestOverride: &conf.StringList{"http", "tls"}, DestOverride: &coreConf.StringList{"http", "tls"},
} }
if config.DisableSniffing { if config.DisableSniffing {
sniffingConfig.Enabled = false sniffingConfig.Enabled = false
@@ -127,16 +128,16 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err) return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
} }
if *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network == "tcp" { if *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network == "tcp" {
if nodeInfo.NodeType == "V2ray" { if nodeInfo.V2ray.Inbounds[0].StreamSetting.TCPSettings != nil {
nodeInfo.V2ray.Inbounds[0].StreamSetting.TCPSettings.AcceptProxyProtocol = config.EnableProxyProtocol nodeInfo.V2ray.Inbounds[0].StreamSetting.TCPSettings.AcceptProxyProtocol = config.EnableProxyProtocol
} else { } else {
tcpSetting := &conf.TCPConfig{ tcpSetting := &coreConf.TCPConfig{
AcceptProxyProtocol: config.EnableProxyProtocol, AcceptProxyProtocol: config.EnableProxyProtocol,
} }
nodeInfo.V2ray.Inbounds[0].StreamSetting.TCPSettings = tcpSetting nodeInfo.V2ray.Inbounds[0].StreamSetting.TCPSettings = tcpSetting
} }
} else if *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network == "ws" { } else if *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network == "ws" {
nodeInfo.V2ray.Inbounds[0].StreamSetting.WSSettings = &conf.WebSocketConfig{ nodeInfo.V2ray.Inbounds[0].StreamSetting.WSSettings = &coreConf.WebSocketConfig{
AcceptProxyProtocol: config.EnableProxyProtocol} AcceptProxyProtocol: config.EnableProxyProtocol}
} }
// Build TLS and XTLS settings // Build TLS and XTLS settings
@@ -147,17 +148,17 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
return nil, err return nil, err
} }
if nodeInfo.TLSType == "tls" { if nodeInfo.TLSType == "tls" {
tlsSettings := &conf.TLSConfig{ tlsSettings := &coreConf.TLSConfig{
RejectUnknownSNI: config.CertConfig.RejectUnknownSni, RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
} }
tlsSettings.Certs = append(tlsSettings.Certs, &conf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600}) tlsSettings.Certs = append(tlsSettings.Certs, &coreConf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
nodeInfo.V2ray.Inbounds[0].StreamSetting.TLSSettings = tlsSettings nodeInfo.V2ray.Inbounds[0].StreamSetting.TLSSettings = tlsSettings
} else if nodeInfo.TLSType == "xtls" { } else if nodeInfo.TLSType == "xtls" {
xtlsSettings := &conf.XTLSConfig{ xtlsSettings := &coreConf.XTLSConfig{
RejectUnknownSNI: config.CertConfig.RejectUnknownSni, RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
} }
xtlsSettings.Certs = append(xtlsSettings.Certs, &conf.XTLSCertConfig{ xtlsSettings.Certs = append(xtlsSettings.Certs, &coreConf.XTLSCertConfig{
CertFile: certFile, CertFile: certFile,
KeyFile: keyFile, KeyFile: keyFile,
OcspStapling: 3600}) OcspStapling: 3600})
@@ -168,7 +169,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
if *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network != "tcp" && if *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network != "tcp" &&
*nodeInfo.V2ray.Inbounds[0].StreamSetting.Network != "ws" && *nodeInfo.V2ray.Inbounds[0].StreamSetting.Network != "ws" &&
config.EnableProxyProtocol { config.EnableProxyProtocol {
sockoptConfig := &conf.SocketConfig{ sockoptConfig := &coreConf.SocketConfig{
AcceptProxyProtocol: config.EnableProxyProtocol, AcceptProxyProtocol: config.EnableProxyProtocol,
} }
nodeInfo.V2ray.Inbounds[0].StreamSetting.SocketSettings = sockoptConfig nodeInfo.V2ray.Inbounds[0].StreamSetting.SocketSettings = sockoptConfig
@@ -178,7 +179,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
return nodeInfo.V2ray.Inbounds[0].Build() return nodeInfo.V2ray.Inbounds[0].Build()
} }
func getCertFile(certConfig *CertConfig) (certFile string, keyFile string, err error) { func getCertFile(certConfig *conf.CertConfig) (certFile string, keyFile string, err error) {
if certConfig.CertMode == "file" { if certConfig.CertMode == "file" {
if certConfig.CertFile == "" || certConfig.KeyFile == "" { if certConfig.CertFile == "" || certConfig.KeyFile == "" {
return "", "", fmt.Errorf("cert file path or key file path not exist") return "", "", fmt.Errorf("cert file path or key file path not exist")
@@ -209,12 +210,12 @@ func getCertFile(certConfig *CertConfig) (certFile string, keyFile string, err e
return "", "", fmt.Errorf("unsupported certmode: %s", certConfig.CertMode) return "", "", fmt.Errorf("unsupported certmode: %s", certConfig.CertMode)
} }
func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboundFallback, error) { func buildVlessFallbacks(fallbackConfigs []*conf.FallBackConfig) ([]*coreConf.VLessInboundFallback, error) {
if fallbackConfigs == nil { if fallbackConfigs == nil {
return nil, fmt.Errorf("you must provide FallBackConfigs") return nil, fmt.Errorf("you must provide FallBackConfigs")
} }
vlessFallBacks := make([]*conf.VLessInboundFallback, len(fallbackConfigs)) vlessFallBacks := make([]*coreConf.VLessInboundFallback, len(fallbackConfigs))
for i, c := range fallbackConfigs { for i, c := range fallbackConfigs {
if c.Dest == "" { if c.Dest == "" {
@@ -226,7 +227,7 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
if err != nil { if err != nil {
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err) return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
} }
vlessFallBacks[i] = &conf.VLessInboundFallback{ vlessFallBacks[i] = &coreConf.VLessInboundFallback{
Name: c.SNI, Name: c.SNI,
Alpn: c.Alpn, Alpn: c.Alpn,
Path: c.Path, Path: c.Path,
@@ -237,12 +238,12 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
return vlessFallBacks, nil return vlessFallBacks, nil
} }
func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInboundFallback, error) { func buildTrojanFallbacks(fallbackConfigs []*conf.FallBackConfig) ([]*coreConf.TrojanInboundFallback, error) {
if fallbackConfigs == nil { if fallbackConfigs == nil {
return nil, fmt.Errorf("you must provide FallBackConfigs") return nil, fmt.Errorf("you must provide FallBackConfigs")
} }
trojanFallBacks := make([]*conf.TrojanInboundFallback, len(fallbackConfigs)) trojanFallBacks := make([]*coreConf.TrojanInboundFallback, len(fallbackConfigs))
for i, c := range fallbackConfigs { for i, c := range fallbackConfigs {
if c.Dest == "" { if c.Dest == "" {
@@ -254,7 +255,7 @@ func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInbo
if err != nil { if err != nil {
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err) return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
} }
trojanFallBacks[i] = &conf.TrojanInboundFallback{ trojanFallBacks[i] = &coreConf.TrojanInboundFallback{
Name: c.SNI, Name: c.SNI,
Alpn: c.Alpn, Alpn: c.Alpn,
Path: c.Path, Path: c.Path,
@@ -1,10 +1,10 @@
package controller_test package node_test
import ( import (
. "github.com/Yuzuki616/V2bX/node"
"testing" "testing"
"github.com/Yuzuki616/V2bX/api" "github.com/Yuzuki616/V2bX/api"
. "github.com/Yuzuki616/V2bX/service/controller"
) )
func TestBuildV2ray(t *testing.T) { func TestBuildV2ray(t *testing.T) {
+102 -89
View File
@@ -1,7 +1,12 @@
package controller package node
import ( import (
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/common/limiter"
"github.com/Yuzuki616/V2bX/conf"
"github.com/Yuzuki616/V2bX/xray"
"github.com/go-resty/resty/v2"
"github.com/goccy/go-json"
"log" "log"
"math" "math"
"reflect" "reflect"
@@ -12,12 +17,11 @@ import (
"github.com/Yuzuki616/V2bX/common/legoCmd" "github.com/Yuzuki616/V2bX/common/legoCmd"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
) )
type Controller struct { type Node struct {
server *core.Instance server *xray.Xray
config *Config config *conf.ControllerConfig
clientInfo api.ClientInfo clientInfo api.ClientInfo
apiClient api.API apiClient api.API
nodeInfo *api.NodeInfo nodeInfo *api.NodeInfo
@@ -25,12 +29,12 @@ type Controller struct {
userList *[]api.UserInfo userList *[]api.UserInfo
nodeInfoMonitorPeriodic *task.Periodic nodeInfoMonitorPeriodic *task.Periodic
userReportPeriodic *task.Periodic userReportPeriodic *task.Periodic
panelType string onlineIpReportPeriodic *task.Periodic
} }
// New return a Controller service with default parameters. // New return a Node service with default parameters.
func New(server *core.Instance, api api.API, config *Config) *Controller { func New(server *xray.Xray, api api.API, config *conf.ControllerConfig) *Node {
controller := &Controller{ controller := &Node{
server: server, server: server,
config: config, config: config,
apiClient: api, apiClient: api,
@@ -39,7 +43,7 @@ func New(server *core.Instance, api api.API, config *Config) *Controller {
} }
// Start implement the Start() function of the service interface // Start implement the Start() function of the service interface
func (c *Controller) Start() error { func (c *Node) Start() error {
c.clientInfo = c.apiClient.Describe() c.clientInfo = c.apiClient.Describe()
// First fetch Node Info // First fetch Node Info
newNodeInfo, err := c.apiClient.GetNodeInfo() newNodeInfo, err := c.apiClient.GetNodeInfo()
@@ -66,7 +70,7 @@ func (c *Controller) Start() error {
} }
//sync controller userList //sync controller userList
c.userList = userInfo c.userList = userInfo
if err := c.AddInboundLimiter(c.Tag, userInfo); err != nil { if err := c.server.AddInboundLimiter(c.Tag, c.nodeInfo, userInfo); err != nil {
log.Print(err) log.Print(err)
} }
// Add Rule Manager // Add Rule Manager
@@ -74,11 +78,11 @@ func (c *Controller) Start() error {
if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil { if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil {
log.Printf("Get rule list filed: %s", err) log.Printf("Get rule list filed: %s", err)
} else if len(*ruleList) > 0 { } else if len(*ruleList) > 0 {
if err := c.UpdateRule(c.Tag, *ruleList); err != nil { if err := c.server.UpdateRule(c.Tag, *ruleList); err != nil {
log.Print(err) log.Print(err)
} }
if len(*protocolRule) > 0 { if len(*protocolRule) > 0 {
if err := c.UpdateProtocolRule(c.Tag, *protocolRule); err != nil { if err := c.server.UpdateProtocolRule(c.Tag, *protocolRule); err != nil {
log.Print(err) log.Print(err)
} }
} }
@@ -105,12 +109,24 @@ func (c *Controller) Start() error {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second) time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.userReportPeriodic.Start() _ = c.userReportPeriodic.Start()
}() }()
if c.config.EnableIpRecorder {
c.onlineIpReportPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * 30,
Execute: c.onlineIpReport,
}
log.Printf("[%s: %d] Start report online ip", c.nodeInfo.NodeType, c.nodeInfo.NodeId)
// delay to start onlineIpReport
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.onlineIpReportPeriodic.Start()
}()
}
runtime.GC() runtime.GC()
return nil return nil
} }
// Close implement the Close() function of the service interface // Close implement the Close() function of the service interface
func (c *Controller) Close() error { func (c *Node) Close() error {
if c.nodeInfoMonitorPeriodic != nil { if c.nodeInfoMonitorPeriodic != nil {
err := c.nodeInfoMonitorPeriodic.Close() err := c.nodeInfoMonitorPeriodic.Close()
if err != nil { if err != nil {
@@ -124,10 +140,16 @@ func (c *Controller) Close() error {
log.Panicf("user report periodic close failed: %s", err) 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)
}
}
return nil return nil
} }
func (c *Controller) nodeInfoMonitor() (err error) { func (c *Node) nodeInfoMonitor() (err error) {
// First fetch Node Info // First fetch Node Info
newNodeInfo, err := c.apiClient.GetNodeInfo() newNodeInfo, err := c.apiClient.GetNodeInfo()
if err != nil { if err != nil {
@@ -156,7 +178,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
} }
nodeInfoChanged = true nodeInfoChanged = true
// Remove Old limiter // Remove Old limiter
if err = c.DeleteInboundLimiter(oldtag); err != nil { if err = c.server.DeleteInboundLimiter(oldtag); err != nil {
log.Print(err) log.Print(err)
return nil return nil
} }
@@ -168,11 +190,11 @@ func (c *Controller) nodeInfoMonitor() (err error) {
if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil { if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil {
log.Printf("Get rule list filed: %s", err) log.Printf("Get rule list filed: %s", err)
} else if len(*ruleList) > 0 { } else if len(*ruleList) > 0 {
if err := c.UpdateRule(c.Tag, *ruleList); err != nil { if err := c.server.UpdateRule(c.Tag, *ruleList); err != nil {
log.Print(err) log.Print(err)
} }
if len(*protocolRule) > 0 { if len(*protocolRule) > 0 {
if err := c.UpdateProtocolRule(c.Tag, *protocolRule); err != nil { if err := c.server.UpdateProtocolRule(c.Tag, *protocolRule); err != nil {
log.Print(err) log.Print(err)
} }
} }
@@ -210,37 +232,37 @@ func (c *Controller) nodeInfoMonitor() (err error) {
} }
newNodeInfo = nil newNodeInfo = nil
// Add Limiter // Add Limiter
if err := c.AddInboundLimiter(c.Tag, c.userList); err != nil { if err := c.server.AddInboundLimiter(c.Tag, c.nodeInfo, c.userList); err != nil {
log.Print(err) log.Print(err)
return nil return nil
} }
runtime.GC() runtime.GC()
} else { } else {
deleted, added := compareUserList(c.userList, newUserInfo) deleted, added := compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 { if len(*deleted) > 0 {
deletedEmail := make([]string, len(deleted)) deletedEmail := make([]string, len(*deleted))
for i := range deleted { for i := range *deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag, deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag,
(*c.userList)[deleted[i]].GetUserEmail(), (*deleted)[i].GetUserEmail(),
(*c.userList)[deleted[i]].UID) (*deleted)[i].UID)
} }
err := c.removeUsers(deletedEmail, c.Tag) err := c.server.RemoveUsers(deletedEmail, c.Tag)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
} }
} }
if len(added) > 0 { if len(*added) > 0 {
err = c.addNewUserFromIndex(newUserInfo, &added, c.nodeInfo) err = c.addNewUser(added, newNodeInfo)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
} }
// Update Limiter // Update Limiter
if err := c.UpdateInboundLimiter(c.Tag, newUserInfo, &added); err != nil { if err := c.server.UpdateInboundLimiter(c.Tag, c.nodeInfo, added); err != nil {
log.Print(err) log.Print(err)
} }
} }
log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeId, log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeId,
len(deleted), len(added)) len(*deleted), len(*added))
c.userList = newUserInfo c.userList = newUserInfo
newUserInfo = nil newUserInfo = nil
runtime.GC() runtime.GC()
@@ -248,24 +270,24 @@ func (c *Controller) nodeInfoMonitor() (err error) {
return nil return nil
} }
func (c *Controller) removeOldTag(oldtag string) (err error) { func (c *Node) removeOldTag(oldtag string) (err error) {
err = c.removeInbound(oldtag) err = c.server.RemoveInbound(oldtag)
if err != nil { if err != nil {
return err return err
} }
err = c.removeOutbound(oldtag) err = c.server.RemoveOutbound(oldtag)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) { func (c *Node) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
inboundConfig, err := InboundBuilder(c.config, newNodeInfo, c.Tag) inboundConfig, err := InboundBuilder(c.config, newNodeInfo, c.Tag)
if err != nil { if err != nil {
return err return err
} }
err = c.addInbound(inboundConfig) err = c.server.AddInbound(inboundConfig)
if err != nil { if err != nil {
return err return err
@@ -275,7 +297,7 @@ func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
return err return err
} }
err = c.addOutbound(outBoundConfig) err = c.server.AddOutbound(outBoundConfig)
if err != nil { if err != nil {
return err return err
@@ -283,7 +305,7 @@ func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
return nil return nil
} }
func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) { func (c *Node) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) {
users := make([]*protocol.User, 0) users := make([]*protocol.User, 0)
if nodeInfo.NodeType == "V2ray" { if nodeInfo.NodeType == "V2ray" {
if nodeInfo.EnableVless { if nodeInfo.EnableVless {
@@ -301,11 +323,11 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
} else if nodeInfo.NodeType == "Trojan" { } else if nodeInfo.NodeType == "Trojan" {
users = c.buildTrojanUsers(userInfo) users = c.buildTrojanUsers(userInfo)
} else if nodeInfo.NodeType == "Shadowsocks" { } else if nodeInfo.NodeType == "Shadowsocks" {
users = c.buildSSUsers(userInfo, nodeInfo.SS.CypherMethod) users = c.buildSSUsers(userInfo, getCipherFromString(nodeInfo.SS.CypherMethod))
} else { } else {
return fmt.Errorf("unsupported node type: %s", nodeInfo.NodeType) return fmt.Errorf("unsupported node type: %s", nodeInfo.NodeType)
} }
err = c.addUsers(users, c.Tag) err = c.server.AddUsers(users, c.Tag)
if err != nil { if err != nil {
return err return err
} }
@@ -313,39 +335,7 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
return nil return nil
} }
func (c *Controller) addNewUserFromIndex(userInfo *[]api.UserInfo, userIndex *[]int, nodeInfo *api.NodeInfo) (err error) { func compareUserList(old, new *[]api.UserInfo) (deleted, added *[]api.UserInfo) {
users := make([]*protocol.User, 0, len(*userIndex))
for _, v := range *userIndex {
if nodeInfo.NodeType == "V2ray" {
if nodeInfo.EnableVless {
users = append(users, c.buildVlessUser(&(*userInfo)[v]))
} else {
alterID := 0
alterID = (*userInfo)[0].V2rayUser.AlterId
if alterID >= 0 && alterID < math.MaxUint16 {
users = append(users, c.buildVmessUser(&(*userInfo)[v], uint16(alterID)))
} else {
users = append(users, c.buildVmessUser(&(*userInfo)[v], 0))
return fmt.Errorf("AlterID should between 0 to 1<<16 - 1, set it to 0 for now")
}
}
} else if nodeInfo.NodeType == "Trojan" {
users = append(users, c.buildTrojanUser(&(*userInfo)[v]))
} else if nodeInfo.NodeType == "Shadowsocks" {
users = append(users, c.buildSSUser(&(*userInfo)[v], nodeInfo.SS.CypherMethod))
} else {
return fmt.Errorf("unsupported node type: %s", nodeInfo.NodeType)
}
}
err = c.addUsers(users, c.Tag)
if err != nil {
return err
}
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(*userIndex))
return nil
}
func compareUserList(old, new *[]api.UserInfo) (deleted, added []int) {
tmp := map[string]struct{}{} tmp := map[string]struct{}{}
tmp2 := map[string]struct{}{} tmp2 := map[string]struct{}{}
for i := range *old { for i := range *old {
@@ -357,7 +347,7 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []int) {
tmp[e] = struct{}{} tmp[e] = struct{}{}
tmp2[e] = struct{}{} tmp2[e] = struct{}{}
if l != len(tmp) { if l != len(tmp) {
added = append(added, i) *added = append(*added, (*new)[i])
l++ l++
} }
} }
@@ -366,18 +356,18 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []int) {
for i := range *old { for i := range *old {
tmp2[(*old)[i].GetUserEmail()] = struct{}{} tmp2[(*old)[i].GetUserEmail()] = struct{}{}
if l != len(tmp2) { if l != len(tmp2) {
deleted = append(deleted, i) *deleted = append(*deleted, (*old)[i])
l++ l++
} }
} }
return deleted, added return deleted, added
} }
func (c *Controller) userInfoMonitor() (err error) { func (c *Node) userInfoMonitor() (err error) {
// Get User traffic // Get User traffic
userTraffic := make([]api.UserTraffic, 0) userTraffic := make([]api.UserTraffic, 0)
for i := range *c.userList { for i := range *c.userList {
up, down := c.getTraffic(c.buildUserTag(&(*c.userList)[i])) up, down := c.server.GetUserTraffic(c.buildUserTag(&(*c.userList)[i]))
if up > 0 || down > 0 { if up > 0 || down > 0 {
userTraffic = append(userTraffic, api.UserTraffic{ userTraffic = append(userTraffic, api.UserTraffic{
UID: (*c.userList)[i].UID, UID: (*c.userList)[i].UID,
@@ -389,26 +379,49 @@ func (c *Controller) userInfoMonitor() (err error) {
err = c.apiClient.ReportUserTraffic(&userTraffic) err = c.apiClient.ReportUserTraffic(&userTraffic)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
} else {
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(userTraffic))
} }
} }
// Report Online info
if onlineDevice, err := c.GetOnlineDevice(c.Tag); err != nil {
log.Print(err)
} else {
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(*onlineDevice))
}
// Report Illegal user
if detectResult, err := c.GetDetectResult(c.Tag); err != nil {
log.Print(err)
} else {
log.Printf("[%s: %d] Report %d illegal behaviors", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(*detectResult))
}
userTraffic = nil userTraffic = nil
runtime.GC() runtime.GC()
return nil return nil
} }
func (c *Controller) buildNodeTag() string { func (c *Node) onlineIpReport() (err error) {
onlineIp, err := c.server.GetOnlineIps(c.Tag)
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)
c.server.ClearOnlineIps(c.Tag)
return nil
}
log.Printf("[Node: %d] Report %d online ip", c.nodeInfo.NodeId, len(*onlineIp))
if rsp.StatusCode() == 200 {
onlineIp = &[]limiter.UserIp{}
err := json.Unmarshal(rsp.Body(), onlineIp)
if err != nil {
log.Print(err)
c.server.ClearOnlineIps(c.Tag)
return nil
}
c.server.UpdateOnlineIps(c.Tag, onlineIp)
log.Printf("[Node: %d] Updated %d online ip", c.nodeInfo.NodeId, len(*onlineIp))
} else {
c.server.ClearOnlineIps(c.Tag)
}
return nil
}
func (c *Node) buildNodeTag() string {
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.NodeId) return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.NodeId)
} }
@@ -1,7 +1,8 @@
package controller_test package node_test
import ( import (
"fmt" "fmt"
. "github.com/Yuzuki616/V2bX/node"
"os" "os"
"os/signal" "os/signal"
"runtime" "runtime"
@@ -9,8 +10,7 @@ import (
"testing" "testing"
"github.com/Yuzuki616/V2bX/api" "github.com/Yuzuki616/V2bX/api"
_ "github.com/Yuzuki616/V2bX/main/distro/all" _ "github.com/Yuzuki616/V2bX/example/distro/all"
. "github.com/Yuzuki616/V2bX/service/controller"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf" "github.com/xtls/xray-core/infra/conf"
) )
@@ -1,8 +1,9 @@
package controller package node
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
conf2 "github.com/Yuzuki616/V2bX/conf"
"github.com/Yuzuki616/V2bX/api" "github.com/Yuzuki616/V2bX/api"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
@@ -11,7 +12,7 @@ import (
) )
//OutboundBuilder build freedom outbund config for addoutbound //OutboundBuilder build freedom outbund config for addoutbound
func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.OutboundHandlerConfig, error) { func OutboundBuilder(config *conf2.ControllerConfig, nodeInfo *api.NodeInfo, tag string) (*core.OutboundHandlerConfig, error) {
outboundDetourConfig := &conf.OutboundDetourConfig{} outboundDetourConfig := &conf.OutboundDetourConfig{}
outboundDetourConfig.Protocol = "freedom" outboundDetourConfig.Protocol = "freedom"
outboundDetourConfig.Tag = tag outboundDetourConfig.Tag = tag
+118
View File
@@ -0,0 +1,118 @@
package node
import (
"fmt"
"strings"
"github.com/Yuzuki616/V2bX/api"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/infra/conf"
"github.com/xtls/xray-core/proxy/shadowsocks"
"github.com/xtls/xray-core/proxy/trojan"
"github.com/xtls/xray-core/proxy/vless"
)
func (c *Node) buildVmessUsers(userInfo *[]api.UserInfo, serverAlterID uint16) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i, user := range *userInfo {
users[i] = c.buildVmessUser(&user, serverAlterID)
}
return users
}
func (c *Node) buildVmessUser(userInfo *api.UserInfo, serverAlterID uint16) (user *protocol.User) {
vmessAccount := &conf.VMessAccount{
ID: userInfo.V2rayUser.Uuid,
AlterIds: serverAlterID,
Security: "auto",
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo), // Uid: InboundTag|email|uid
Account: serial.ToTypedMessage(vmessAccount.Build()),
}
return user
}
func (c *Node) buildVlessUsers(userInfo *[]api.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i := range *userInfo {
users[i] = c.buildVlessUser(&(*userInfo)[i])
}
return users
}
func (c *Node) buildVlessUser(userInfo *api.UserInfo) (user *protocol.User) {
vlessAccount := &vless.Account{
Id: userInfo.V2rayUser.Uuid,
Flow: "xtls-rprx-direct",
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo),
Account: serial.ToTypedMessage(vlessAccount),
}
return user
}
func (c *Node) buildTrojanUsers(userInfo *[]api.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i := range *userInfo {
users[i] = c.buildTrojanUser(&(*userInfo)[i])
}
return users
}
func (c *Node) buildTrojanUser(userInfo *api.UserInfo) (user *protocol.User) {
trojanAccount := &trojan.Account{
Password: userInfo.TrojanUser.Password,
Flow: "xtls-rprx-direct",
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo),
Account: serial.ToTypedMessage(trojanAccount),
}
return user
}
func getCipherFromString(c string) shadowsocks.CipherType {
switch strings.ToLower(c) {
case "aes-128-gcm", "aead_aes_128_gcm":
return shadowsocks.CipherType_AES_128_GCM
case "aes-256-gcm", "aead_aes_256_gcm":
return shadowsocks.CipherType_AES_256_GCM
case "chacha20-poly1305", "aead_chacha20_poly1305", "chacha20-ietf-poly1305":
return shadowsocks.CipherType_CHACHA20_POLY1305
case "none", "plain":
return shadowsocks.CipherType_NONE
default:
return shadowsocks.CipherType_UNKNOWN
}
}
func (c *Node) buildSSUsers(userInfo *[]api.UserInfo, cypher shadowsocks.CipherType) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i := range *userInfo {
c.buildSSUser(&(*userInfo)[i], cypher)
}
return users
}
func (c *Node) buildSSUser(userInfo *api.UserInfo, cypher shadowsocks.CipherType) (user *protocol.User) {
ssAccount := &shadowsocks.Account{
Password: userInfo.Secret,
CipherType: cypher,
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo),
Account: serial.ToTypedMessage(ssAccount),
}
return user
}
func (c *Node) buildUserTag(user *api.UserInfo) string {
return fmt.Sprintf("%s|%s|%d", c.Tag, user.GetUserEmail(), user.UID)
}
-36
View File
@@ -1,36 +0,0 @@
package panel
import (
"github.com/Yuzuki616/V2bX/api"
"github.com/Yuzuki616/V2bX/service/controller"
)
type Config struct {
LogConfig *LogConfig `mapstructure:"Log"`
DnsConfigPath string `mapstructure:"DnsConfigPath"`
InboundConfigPath string `mapstructure:"InboundConfigPath"`
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
RouteConfigPath string `mapstructure:"RouteConfigPath"`
ConnetionConfig *ConnetionConfig `mapstructure:"ConnetionConfig"`
NodesConfig []*NodesConfig `mapstructure:"Nodes"`
}
type NodesConfig struct {
//PanelType string `mapstructure:"PanelType"`
ApiConfig *api.Config `mapstructure:"ApiConfig"`
ControllerConfig *controller.Config `mapstructure:"ControllerConfig"`
}
type LogConfig struct {
Level string `mapstructure:"Level"`
AccessPath string `mapstructure:"AccessPath"`
ErrorPath string `mapstructure:"ErrorPath"`
}
type ConnetionConfig struct {
Handshake uint32 `mapstructure:"handshake"`
ConnIdle uint32 `mapstructure:"connIdle"`
UplinkOnly uint32 `mapstructure:"uplinkOnly"`
DownlinkOnly uint32 `mapstructure:"downlinkOnly"`
BufferSize int32 `mapstructure:"bufferSize"`
}
-30
View File
@@ -1,30 +0,0 @@
package panel
import "github.com/Yuzuki616/V2bX/service/controller"
func getDefaultLogConfig() *LogConfig {
return &LogConfig{
Level: "none",
AccessPath: "",
ErrorPath: "",
}
}
func getDefaultConnetionConfig() *ConnetionConfig {
return &ConnetionConfig{
Handshake: 4,
ConnIdle: 30,
UplinkOnly: 2,
DownlinkOnly: 4,
BufferSize: 64,
}
}
func getDefaultControllerConfig() *controller.Config {
return &controller.Config{
ListenIP: "0.0.0.0",
SendIP: "0.0.0.0",
UpdatePeriodic: 60,
DNSType: "AsIs",
}
}
-219
View File
@@ -1,219 +0,0 @@
package panel
import (
"encoding/json"
"github.com/Yuzuki616/V2bX/app/mydispatcher"
io "io/ioutil"
"log"
"sync"
"github.com/Yuzuki616/V2bX/api"
_ "github.com/Yuzuki616/V2bX/main/distro/all"
"github.com/Yuzuki616/V2bX/service"
"github.com/Yuzuki616/V2bX/service/controller"
"github.com/imdario/mergo"
"github.com/r3labs/diff/v2"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
)
// Panel Structure
type Panel struct {
access sync.Mutex
panelConfig *Config
Server *core.Instance
Service []service.Service
Running bool
}
func New(panelConfig *Config) *Panel {
p := &Panel{panelConfig: panelConfig}
return p
}
func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
// Log Config
coreLogConfig := &conf.LogConfig{}
logConfig := getDefaultLogConfig()
if panelConfig.LogConfig != nil {
if _, err := diff.Merge(logConfig, panelConfig.LogConfig, logConfig); err != nil {
log.Panicf("Read Log config failed: %s", err)
}
}
coreLogConfig.LogLevel = logConfig.Level
coreLogConfig.AccessLog = logConfig.AccessPath
coreLogConfig.ErrorLog = logConfig.ErrorPath
// DNS config
coreDnsConfig := &conf.DNSConfig{}
if panelConfig.DnsConfigPath != "" {
if data, err := io.ReadFile(panelConfig.DnsConfigPath); err != nil {
log.Panicf("Failed to read DNS config file at: %s", panelConfig.DnsConfigPath)
} else {
if err = json.Unmarshal(data, coreDnsConfig); err != nil {
log.Panicf("Failed to unmarshal DNS config: %s", panelConfig.DnsConfigPath)
}
}
}
dnsConfig, err := coreDnsConfig.Build()
if err != nil {
log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err)
}
// Routing config
coreRouterConfig := &conf.RouterConfig{}
if panelConfig.RouteConfigPath != "" {
if data, err := io.ReadFile(panelConfig.RouteConfigPath); err != nil {
log.Panicf("Failed to read Routing config file at: %s", panelConfig.RouteConfigPath)
} else {
if err = json.Unmarshal(data, coreRouterConfig); err != nil {
log.Panicf("Failed to unmarshal Routing config: %s", panelConfig.RouteConfigPath)
}
}
}
routeConfig, err := coreRouterConfig.Build()
if err != nil {
log.Panicf("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html for help: %s", err)
}
// Custom Inbound config
var coreCustomInboundConfig []conf.InboundDetourConfig
if panelConfig.InboundConfigPath != "" {
if data, err := io.ReadFile(panelConfig.InboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Inbound config file at: %s", panelConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomInboundConfig); err != nil {
log.Panicf("Failed to unmarshal Custom Inbound config: %s", panelConfig.OutboundConfigPath)
}
}
}
var inBoundConfig []*core.InboundHandlerConfig
for _, config := range coreCustomInboundConfig {
oc, err := config.Build()
if err != nil {
log.Panicf("Failed to understand Inbound config, Please check: https://xtls.github.io/config/inbound.html for help: %s", err)
}
inBoundConfig = append(inBoundConfig, oc)
}
// Custom Outbound config
var coreCustomOutboundConfig []conf.OutboundDetourConfig
if panelConfig.OutboundConfigPath != "" {
if data, err := io.ReadFile(panelConfig.OutboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Outbound config file at: %s", panelConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomOutboundConfig); err != nil {
log.Panicf("Failed to unmarshal Custom Outbound config: %s", panelConfig.OutboundConfigPath)
}
}
}
var outBoundConfig []*core.OutboundHandlerConfig
for _, config := range coreCustomOutboundConfig {
oc, err := config.Build()
if err != nil {
log.Panicf("Failed to understand Outbound config, Please check: https://xtls.github.io/config/outbound.html for help: %s", err)
}
outBoundConfig = append(outBoundConfig, oc)
}
// Policy config
levelPolicyConfig := parseConnectionConfig(panelConfig.ConnetionConfig)
corePolicyConfig := &conf.PolicyConfig{}
corePolicyConfig.Levels = map[uint32]*conf.Policy{0: levelPolicyConfig}
policyConfig, _ := corePolicyConfig.Build()
// Build Core Config
config := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(coreLogConfig.Build()),
serial.ToTypedMessage(&mydispatcher.Config{}),
serial.ToTypedMessage(&stats.Config{}),
serial.ToTypedMessage(&proxyman.InboundConfig{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
serial.ToTypedMessage(policyConfig),
serial.ToTypedMessage(dnsConfig),
serial.ToTypedMessage(routeConfig),
},
Inbound: inBoundConfig,
Outbound: outBoundConfig,
}
server, err := core.New(config)
if err != nil {
log.Panicf("failed to create instance: %s", err)
}
log.Printf("Xray Core Version: %s", core.Version())
return server
}
// Start Start the panel
func (p *Panel) Start() {
p.access.Lock()
defer p.access.Unlock()
log.Print("Start the panel..")
// Load Core
server := p.loadCore(p.panelConfig)
if err := server.Start(); err != nil {
log.Panicf("Failed to start instance: %s", err)
}
p.Server = server
// Load Nodes config
for _, nodeConfig := range p.panelConfig.NodesConfig {
var apiClient = api.New(nodeConfig.ApiConfig)
var controllerService service.Service
// Register controller service
controllerConfig := getDefaultControllerConfig()
if nodeConfig.ControllerConfig != nil {
if err := mergo.Merge(controllerConfig, nodeConfig.ControllerConfig, mergo.WithOverride); err != nil {
log.Panicf("Read Controller Config Failed")
}
}
controllerService = controller.New(server, apiClient, controllerConfig)
p.Service = append(p.Service, controllerService)
}
// Start all the service
for _, s := range p.Service {
err := s.Start()
if err != nil {
log.Panicf("Panel Start fialed: %s", err)
}
}
p.Running = true
return
}
// Close Close the panel
func (p *Panel) Close() {
p.access.Lock()
defer p.access.Unlock()
for _, s := range p.Service {
err := s.Close()
if err != nil {
log.Panicf("Panel Close fialed: %s", err)
}
}
p.Service = nil
p.Server.Close()
p.Running = false
return
}
func parseConnectionConfig(c *ConnetionConfig) (policy *conf.Policy) {
connetionConfig := getDefaultConnetionConfig()
if c != nil {
if _, err := diff.Merge(connetionConfig, c, connetionConfig); err != nil {
log.Panicf("Read ConnetionConfig failed: %s", err)
}
}
policy = &conf.Policy{
StatsUserUplink: true,
StatsUserDownlink: true,
Handshake: &connetionConfig.Handshake,
ConnectionIdle: &connetionConfig.ConnIdle,
UplinkOnly: &connetionConfig.UplinkOnly,
DownlinkOnly: &connetionConfig.DownlinkOnly,
BufferSize: &connetionConfig.BufferSize,
}
return
}
-170
View File
@@ -1,170 +0,0 @@
package controller
import (
"context"
"fmt"
"github.com/Yuzuki616/V2bX/api"
"github.com/Yuzuki616/V2bX/app/mydispatcher"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy"
)
func (c *Controller) removeInbound(tag string) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
err := inboundManager.RemoveHandler(context.Background(), tag)
return err
}
func (c *Controller) removeOutbound(tag string) error {
outboundManager := c.server.GetFeature(outbound.ManagerType()).(outbound.Manager)
err := outboundManager.RemoveHandler(context.Background(), tag)
return err
}
func (c *Controller) addInbound(config *core.InboundHandlerConfig) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
rawHandler, err := core.CreateObject(c.server, config)
if err != nil {
return err
}
handler, ok := rawHandler.(inbound.Handler)
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := inboundManager.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
func (c *Controller) addOutbound(config *core.OutboundHandlerConfig) error {
outboundManager := c.server.GetFeature(outbound.ManagerType()).(outbound.Manager)
rawHandler, err := core.CreateObject(c.server, config)
if err != nil {
return err
}
handler, ok := rawHandler.(outbound.Handler)
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := outboundManager.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
func (c *Controller) addUsers(users []*protocol.User, tag string) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
handler, err := inboundManager.GetHandler(context.Background(), tag)
if err != nil {
return fmt.Errorf("no such inbound tag: %s", err)
}
inboundInstance, ok := handler.(proxy.GetInbound)
if !ok {
return fmt.Errorf("handler %s is not implement proxy.GetInbound", tag)
}
userManager, ok := inboundInstance.GetInbound().(proxy.UserManager)
if !ok {
return fmt.Errorf("handler %s is not implement proxy.UserManager", err)
}
for _, item := range users {
mUser, err := item.ToMemoryUser()
if err != nil {
return err
}
err = userManager.AddUser(context.Background(), mUser)
if err != nil {
return err
}
}
return nil
}
func (c *Controller) removeUsers(users []string, tag string) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
handler, err := inboundManager.GetHandler(context.Background(), tag)
if err != nil {
return fmt.Errorf("no such inbound tag: %s", err)
}
inboundInstance, ok := handler.(proxy.GetInbound)
if !ok {
return fmt.Errorf("handler %s is not implement proxy.GetInbound", tag)
}
userManager, ok := inboundInstance.GetInbound().(proxy.UserManager)
if !ok {
return fmt.Errorf("handler %s is not implement proxy.UserManager", err)
}
for _, email := range users {
err = userManager.RemoveUser(context.Background(), email)
if err != nil {
return err
}
}
return nil
}
func (c *Controller) getTraffic(email string) (up int64, down int64) {
upName := "user>>>" + email + ">>>traffic>>>uplink"
downName := "user>>>" + email + ">>>traffic>>>downlink"
statsManager := c.server.GetFeature(stats.ManagerType()).(stats.Manager)
upCounter := statsManager.GetCounter(upName)
downCounter := statsManager.GetCounter(downName)
if upCounter != nil {
up = upCounter.Value()
upCounter.Set(0)
}
if downCounter != nil {
down = downCounter.Value()
downCounter.Set(0)
}
return up, down
}
func (c *Controller) AddInboundLimiter(tag string, userList *[]api.UserInfo) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.Limiter.AddInboundLimiter(tag, c.nodeInfo, userList)
return err
}
func (c *Controller) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserInfo, usersIndex *[]int) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.Limiter.UpdateInboundLimiter(tag, c.nodeInfo, updatedUserList, usersIndex)
return err
}
func (c *Controller) DeleteInboundLimiter(tag string) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.Limiter.DeleteInboundLimiter(tag)
return err
}
func (c *Controller) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
return dispather.Limiter.GetOnlineDevice(tag)
}
func (c *Controller) UpdateRule(tag string, newRuleList []api.DetectRule) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.RuleManager.UpdateRule(tag, newRuleList)
return err
}
func (c *Controller) UpdateProtocolRule(tag string, newRuleList []string) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.RuleManager.UpdateProtocolRule(tag, newRuleList)
return err
}
func (c *Controller) GetDetectResult(tag string) (*[]api.DetectResult, error) {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
return dispather.RuleManager.GetDetectResult(tag)
}
-154
View File
@@ -1,154 +0,0 @@
package controller
import (
"fmt"
"strings"
"github.com/Yuzuki616/V2bX/api"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/infra/conf"
"github.com/xtls/xray-core/proxy/shadowsocks"
"github.com/xtls/xray-core/proxy/trojan"
"github.com/xtls/xray-core/proxy/vless"
)
func (c *Controller) buildVmessUsers(userInfo *[]api.UserInfo, serverAlterID uint16) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i, user := range *userInfo {
vmessAccount := &conf.VMessAccount{
ID: user.V2rayUser.Uuid,
AlterIds: serverAlterID,
Security: "auto",
}
users[i] = &protocol.User{
Level: 0,
Email: c.buildUserTag(&user), // Email: InboundTag|email|uid
Account: serial.ToTypedMessage(vmessAccount.Build()),
}
}
return users
}
func (c *Controller) buildVmessUser(userInfo *api.UserInfo, serverAlterID uint16) (user *protocol.User) {
vmessAccount := &conf.VMessAccount{
ID: userInfo.V2rayUser.Uuid,
AlterIds: serverAlterID,
Security: "auto",
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo), // Email: InboundTag|email|uid
Account: serial.ToTypedMessage(vmessAccount.Build()),
}
return user
}
func (c *Controller) buildVlessUsers(userInfo *[]api.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i, user := range *userInfo {
vlessAccount := &vless.Account{
Id: user.V2rayUser.Uuid,
Flow: "xtls-rprx-direct",
}
users[i] = &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(vlessAccount),
}
}
return users
}
func (c *Controller) buildVlessUser(userInfo *api.UserInfo) (user *protocol.User) {
vlessAccount := &vless.Account{
Id: userInfo.V2rayUser.Uuid,
Flow: "xtls-rprx-direct",
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo),
Account: serial.ToTypedMessage(vlessAccount),
}
return user
}
func (c *Controller) buildTrojanUsers(userInfo *[]api.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i, user := range *userInfo {
trojanAccount := &trojan.Account{
Password: user.TrojanUser.Password,
Flow: "xtls-rprx-direct",
}
users[i] = &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(trojanAccount),
}
}
return users
}
func (c *Controller) buildTrojanUser(userInfo *api.UserInfo) (user *protocol.User) {
trojanAccount := &trojan.Account{
Password: userInfo.TrojanUser.Password,
Flow: "xtls-rprx-direct",
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo),
Account: serial.ToTypedMessage(trojanAccount),
}
return user
}
func cipherFromString(c string) shadowsocks.CipherType {
switch strings.ToLower(c) {
case "aes-128-gcm", "aead_aes_128_gcm":
return shadowsocks.CipherType_AES_128_GCM
case "aes-256-gcm", "aead_aes_256_gcm":
return shadowsocks.CipherType_AES_256_GCM
case "chacha20-poly1305", "aead_chacha20_poly1305", "chacha20-ietf-poly1305":
return shadowsocks.CipherType_CHACHA20_POLY1305
case "none", "plain":
return shadowsocks.CipherType_NONE
default:
return shadowsocks.CipherType_UNKNOWN
}
}
func (c *Controller) buildSSUsers(userInfo *[]api.UserInfo, method string) (users []*protocol.User) {
users = make([]*protocol.User, 0)
cypherMethod := cipherFromString(method)
for _, user := range *userInfo {
ssAccount := &shadowsocks.Account{
Password: user.Secret,
CipherType: cypherMethod,
}
users = append(users, &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(ssAccount),
})
}
return users
}
func (c *Controller) buildSSUser(userInfo *api.UserInfo, method string) (user *protocol.User) {
cypherMethod := cipherFromString(method)
ssAccount := &shadowsocks.Account{
Password: userInfo.Secret,
CipherType: cypherMethod,
}
user = &protocol.User{
Level: 0,
Email: c.buildUserTag(userInfo),
Account: serial.ToTypedMessage(ssAccount),
}
return user
}
func (c *Controller) buildUserTag(user *api.UserInfo) string {
return fmt.Sprintf("%s|%s|%d", c.Tag, user.GetUserEmail(), user.UID)
}
-16
View File
@@ -1,16 +0,0 @@
// Package service contains all the services used by XrayR
// To implement an service, one needs to implement the interface below.
package service
// Service is the interface of all the services running in the panel
type Service interface {
Start() error
Close() error
Restart
}
// Restart the service
type Restart interface {
Start() error
Close() error
}
@@ -5,7 +5,7 @@ import (
// Required features. Can't remove unless there is replacements. // Required features. Can't remove unless there is replacements.
// _ "github.com/xtls/xray-core/app/dispatcher" // _ "github.com/xtls/xray-core/app/dispatcher"
_ "github.com/Yuzuki616/V2bX/app/mydispatcher" _ "github.com/Yuzuki616/V2bX/app/dispatcher"
_ "github.com/xtls/xray-core/app/proxyman/inbound" _ "github.com/xtls/xray-core/app/proxyman/inbound"
_ "github.com/xtls/xray-core/app/proxyman/outbound" _ "github.com/xtls/xray-core/app/proxyman/outbound"
@@ -29,13 +29,6 @@ import (
_ "github.com/xtls/xray-core/proxy/dns" _ "github.com/xtls/xray-core/proxy/dns"
_ "github.com/xtls/xray-core/proxy/dokodemo" _ "github.com/xtls/xray-core/proxy/dokodemo"
_ "github.com/xtls/xray-core/proxy/freedom" _ "github.com/xtls/xray-core/proxy/freedom"
_ "github.com/xtls/xray-core/proxy/http"
_ "github.com/xtls/xray-core/proxy/mtproto"
_ "github.com/xtls/xray-core/proxy/shadowsocks"
_ "github.com/xtls/xray-core/proxy/socks"
_ "github.com/xtls/xray-core/proxy/trojan"
_ "github.com/xtls/xray-core/proxy/vless/inbound"
_ "github.com/xtls/xray-core/proxy/vless/outbound"
_ "github.com/xtls/xray-core/proxy/vmess/inbound" _ "github.com/xtls/xray-core/proxy/vmess/inbound"
_ "github.com/xtls/xray-core/proxy/vmess/outbound" _ "github.com/xtls/xray-core/proxy/vmess/outbound"
@@ -66,7 +59,4 @@ import (
// Load config from file or http(s) // Load config from file or http(s)
_ "github.com/xtls/xray-core/main/confloader/external" _ "github.com/xtls/xray-core/main/confloader/external"
// Commands
_ "github.com/xtls/xray-core/main/commands/all"
) )
+61
View File
@@ -0,0 +1,61 @@
package xray
import (
"context"
"fmt"
"github.com/Yuzuki616/V2bX/api"
"github.com/Yuzuki616/V2bX/app/dispatcher"
"github.com/Yuzuki616/V2bX/common/limiter"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/routing"
)
func (p *Xray) RemoveInbound(tag string) error {
inboundManager := p.Server.GetFeature(inbound.ManagerType()).(inbound.Manager)
err := inboundManager.RemoveHandler(context.Background(), tag)
return err
}
func (p *Xray) AddInbound(config *core.InboundHandlerConfig) error {
inboundManager := p.Server.GetFeature(inbound.ManagerType()).(inbound.Manager)
rawHandler, err := core.CreateObject(p.Server, config)
if err != nil {
return err
}
handler, ok := rawHandler.(inbound.Handler)
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := inboundManager.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
func (p *Xray) AddInboundLimiter(tag string, nodeInfo *api.NodeInfo, userList *[]api.UserInfo) error {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
err := dispather.Limiter.AddInboundLimiter(tag, nodeInfo, userList)
return err
}
func (p *Xray) GetInboundLimiter(tag string) (*limiter.InboundInfo, error) {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
limit, ok := dispather.Limiter.InboundInfo.Load(tag)
if ok {
return limit.(*limiter.InboundInfo), nil
}
return nil, fmt.Errorf("not found limiter")
}
func (p *Xray) UpdateInboundLimiter(tag string, nodeInfo *api.NodeInfo, updatedUserList *[]api.UserInfo) error {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
err := dispather.Limiter.UpdateInboundLimiter(tag, nodeInfo, updatedUserList)
return err
}
func (p *Xray) DeleteInboundLimiter(tag string) error {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
err := dispather.Limiter.DeleteInboundLimiter(tag)
return err
}
+30
View File
@@ -0,0 +1,30 @@
package xray
import (
"context"
"fmt"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/outbound"
)
func (p *Xray) RemoveOutbound(tag string) error {
outboundManager := p.Server.GetFeature(outbound.ManagerType()).(outbound.Manager)
err := outboundManager.RemoveHandler(context.Background(), tag)
return err
}
func (p *Xray) AddOutbound(config *core.OutboundHandlerConfig) error {
outboundManager := p.Server.GetFeature(outbound.ManagerType()).(outbound.Manager)
rawHandler, err := core.CreateObject(p.Server, config)
if err != nil {
return err
}
handler, ok := rawHandler.(outbound.Handler)
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := outboundManager.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
+24
View File
@@ -0,0 +1,24 @@
package xray
import (
"github.com/Yuzuki616/V2bX/api"
"github.com/Yuzuki616/V2bX/app/dispatcher"
"github.com/xtls/xray-core/features/routing"
)
func (p *Xray) UpdateRule(tag string, newRuleList []api.DetectRule) error {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
err := dispather.RuleManager.UpdateRule(tag, newRuleList)
return err
}
func (p *Xray) UpdateProtocolRule(tag string, newRuleList []string) error {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
err := dispather.RuleManager.UpdateProtocolRule(tag, newRuleList)
return err
}
func (p *Xray) GetDetectResult(tag string) (*[]api.DetectResult, error) {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
return dispather.RuleManager.GetDetectResult(tag)
}
+94
View File
@@ -0,0 +1,94 @@
package xray
import (
"context"
"fmt"
"github.com/Yuzuki616/V2bX/app/dispatcher"
"github.com/Yuzuki616/V2bX/common/limiter"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy"
)
func (p *Xray) GetUserManager(tag string) (proxy.UserManager, error) {
inboundManager := p.Server.GetFeature(inbound.ManagerType()).(inbound.Manager)
handler, err := inboundManager.GetHandler(context.Background(), tag)
if err != nil {
return nil, fmt.Errorf("no such inbound tag: %s", err)
}
inboundInstance, ok := handler.(proxy.GetInbound)
if !ok {
return nil, fmt.Errorf("handler %s is not implement proxy.GetInbound", tag)
}
userManager, ok := inboundInstance.GetInbound().(proxy.UserManager)
if !ok {
return nil, fmt.Errorf("handler %s is not implement proxy.UserManager", tag)
}
return userManager, nil
}
func (p *Xray) AddUsers(users []*protocol.User, tag string) error {
userManager, err := p.GetUserManager(tag)
if err != nil {
return fmt.Errorf("get user manager error: %s", err)
}
for _, item := range users {
mUser, err := item.ToMemoryUser()
if err != nil {
return err
}
err = userManager.AddUser(context.Background(), mUser)
if err != nil {
return err
}
}
return nil
}
func (p *Xray) RemoveUsers(users []string, tag string) error {
userManager, err := p.GetUserManager(tag)
if err != nil {
return fmt.Errorf("get user manager error: %s", err)
}
for _, email := range users {
err = userManager.RemoveUser(context.Background(), email)
if err != nil {
return err
}
}
return nil
}
func (p *Xray) GetUserTraffic(email string) (up int64, down int64) {
upName := "user>>>" + email + ">>>traffic>>>uplink"
downName := "user>>>" + email + ">>>traffic>>>downlink"
statsManager := p.Server.GetFeature(stats.ManagerType()).(stats.Manager)
upCounter := statsManager.GetCounter(upName)
downCounter := statsManager.GetCounter(downName)
if upCounter != nil {
up = upCounter.Value()
upCounter.Set(0)
}
if downCounter != nil {
down = downCounter.Value()
downCounter.Set(0)
}
return up, down
}
func (p *Xray) GetOnlineIps(tag string) (*[]limiter.UserIp, error) {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
return dispather.Limiter.GetOnlineUserIp(tag)
}
func (p *Xray) UpdateOnlineIps(tag string, ips *[]limiter.UserIp) {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
dispather.Limiter.UpdateOnlineUserIP(tag, ips)
}
func (p *Xray) ClearOnlineIps(tag string) {
dispather := p.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
dispather.Limiter.ClearOnlineUserIP(tag)
}
+161
View File
@@ -0,0 +1,161 @@
package xray
import (
"encoding/json"
"github.com/Yuzuki616/V2bX/app/dispatcher"
"github.com/Yuzuki616/V2bX/conf"
_ "github.com/Yuzuki616/V2bX/xray/distro/all"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
coreConf "github.com/xtls/xray-core/infra/conf"
io "io/ioutil"
"log"
"sync"
)
// Xray Structure
type Xray struct {
access sync.Mutex
Server *core.Instance
}
func New(c *conf.Conf) *Xray {
return &Xray{Server: getCore(c)}
}
func parseConnectionConfig(c *conf.ConnetionConfig) (policy *coreConf.Policy) {
policy = &coreConf.Policy{
StatsUserUplink: true,
StatsUserDownlink: true,
Handshake: &c.Handshake,
ConnectionIdle: &c.ConnIdle,
UplinkOnly: &c.UplinkOnly,
DownlinkOnly: &c.DownlinkOnly,
BufferSize: &c.BufferSize,
}
return
}
func getCore(v2bXConfig *conf.Conf) *core.Instance {
// Log Config
coreLogConfig := &coreConf.LogConfig{}
coreLogConfig.LogLevel = v2bXConfig.LogConfig.Level
coreLogConfig.AccessLog = v2bXConfig.LogConfig.AccessPath
coreLogConfig.ErrorLog = v2bXConfig.LogConfig.ErrorPath
// DNS config
coreDnsConfig := &coreConf.DNSConfig{}
if v2bXConfig.DnsConfigPath != "" {
if data, err := io.ReadFile(v2bXConfig.DnsConfigPath); err != nil {
log.Panicf("Failed to read DNS config file at: %s", v2bXConfig.DnsConfigPath)
} else {
if err = json.Unmarshal(data, coreDnsConfig); err != nil {
log.Panicf("Failed to unmarshal DNS config: %s", v2bXConfig.DnsConfigPath)
}
}
}
dnsConfig, err := coreDnsConfig.Build()
if err != nil {
log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err)
}
// Routing config
coreRouterConfig := &coreConf.RouterConfig{}
if v2bXConfig.RouteConfigPath != "" {
if data, err := io.ReadFile(v2bXConfig.RouteConfigPath); err != nil {
log.Panicf("Failed to read Routing config file at: %s", v2bXConfig.RouteConfigPath)
} else {
if err = json.Unmarshal(data, coreRouterConfig); err != nil {
log.Panicf("Failed to unmarshal Routing config: %s", v2bXConfig.RouteConfigPath)
}
}
}
routeConfig, err := coreRouterConfig.Build()
if err != nil {
log.Panicf("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html for help: %s", err)
}
// Custom Inbound config
var coreCustomInboundConfig []coreConf.InboundDetourConfig
if v2bXConfig.InboundConfigPath != "" {
if data, err := io.ReadFile(v2bXConfig.InboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Inbound config file at: %s", v2bXConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomInboundConfig); err != nil {
log.Panicf("Failed to unmarshal Custom Inbound config: %s", v2bXConfig.OutboundConfigPath)
}
}
}
var inBoundConfig []*core.InboundHandlerConfig
for _, config := range coreCustomInboundConfig {
oc, err := config.Build()
if err != nil {
log.Panicf("Failed to understand Inbound config, Please check: https://xtls.github.io/config/inbound.html for help: %s", err)
}
inBoundConfig = append(inBoundConfig, oc)
}
// Custom Outbound config
var coreCustomOutboundConfig []coreConf.OutboundDetourConfig
if v2bXConfig.OutboundConfigPath != "" {
if data, err := io.ReadFile(v2bXConfig.OutboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Outbound config file at: %s", v2bXConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomOutboundConfig); err != nil {
log.Panicf("Failed to unmarshal Custom Outbound config: %s", v2bXConfig.OutboundConfigPath)
}
}
}
var outBoundConfig []*core.OutboundHandlerConfig
for _, config := range coreCustomOutboundConfig {
oc, err := config.Build()
if err != nil {
log.Panicf("Failed to understand Outbound config, Please check: https://xtls.github.io/config/outbound.html for help: %s", err)
}
outBoundConfig = append(outBoundConfig, oc)
}
// Policy config
levelPolicyConfig := parseConnectionConfig(v2bXConfig.ConnectionConfig)
corePolicyConfig := &coreConf.PolicyConfig{}
corePolicyConfig.Levels = map[uint32]*coreConf.Policy{0: levelPolicyConfig}
policyConfig, _ := corePolicyConfig.Build()
// Build Xray conf
config := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(coreLogConfig.Build()),
serial.ToTypedMessage(&dispatcher.Config{}),
serial.ToTypedMessage(&stats.Config{}),
serial.ToTypedMessage(&proxyman.InboundConfig{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
serial.ToTypedMessage(policyConfig),
serial.ToTypedMessage(dnsConfig),
serial.ToTypedMessage(routeConfig),
},
Inbound: inBoundConfig,
Outbound: outBoundConfig,
}
server, err := core.New(config)
if err != nil {
log.Panicf("failed to create instance: %s", err)
}
log.Printf("Xray Version: %s", core.Version())
return server
}
// Start the Xray
func (p *Xray) Start() {
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
}
// Close the core
func (p *Xray) Close() {
p.access.Lock()
defer p.access.Unlock()
p.Server.Close()
return
}