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
main/main
main/XrayR
main/XrayR*
main/mytest
main/access.logo
main/error.log
example/config.yml
example/main
example/XrayR
example/XrayR*
example/mytest
example/access.logo
example/error.log
api/chooseparser.go.bak
common/Inboundbuilder/.lego/
common/legocmd/.lego/
.vscode/launch.json
main/.lego
main/cert
main/config.yml
example/.lego
example/cert
example/config.yml
./vscode
.idea/*
+2 -17
View File
@@ -4,6 +4,7 @@
package api
import (
"github.com/Yuzuki616/V2bX/conf"
"github.com/go-resty/resty/v2"
"log"
"strconv"
@@ -13,21 +14,6 @@ import (
// 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 {
APIHost string
NodeID int
@@ -53,8 +39,7 @@ type Client struct {
NodeRuleRspMd5 [16]byte
}
func New(apiConfig *Config) API {
func New(apiConfig *conf.ApiConfig) API {
client := resty.New()
client.SetRetryCount(3)
if apiConfig.Timeout > 0 {
@@ -4,7 +4,7 @@
// protoc v3.19.4
// source: app/mydispatcher/config.proto
package mydispatcher
package dispatcher
import (
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
@@ -1,4 +1,4 @@
// 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
@@ -1,4 +1,4 @@
package mydispatcher
package dispatcher
import "github.com/xtls/xray-core/common/errors"
@@ -1,4 +1,4 @@
package mydispatcher
package dispatcher
import (
"context"
@@ -1,4 +1,4 @@
package mydispatcher
package dispatcher
import (
"context"
@@ -1,4 +1,4 @@
package mydispatcher
package dispatcher
import (
"github.com/xtls/xray-core/common"
@@ -1,4 +1,4 @@
package mydispatcher_test
package dispatcher_test
import (
"testing"
+74 -34
View File
@@ -19,9 +19,9 @@ type UserInfo struct {
type InboundInfo struct {
Tag string
NodeSpeedLimit uint64
UserInfo *sync.Map // Key: Email value: UserInfo
BucketHub *sync.Map // key: Email, value: *ratelimit.Bucket
UserOnlineIP *sync.Map // Key: Email Value: *sync.Map: Key: IP, Value: UID
UserInfo *sync.Map // Key: Uid value: UserInfo
BucketHub *sync.Map // key: Uid, value: *ratelimit.Bucket
UserOnlineIP *sync.Map // Key: Uid Value: *sync.Map: Key: IP, Value: bool
}
type Limiter struct {
@@ -49,7 +49,7 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeInfo *api.NodeInfo, userList
if (*userList)[i].DeviceLimit == 0 {
(*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,
SpeedLimit: nodeInfo.SpeedLimit,
DeviceLimit: nodeInfo.DeviceLimit,
@@ -60,24 +60,19 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeInfo *api.NodeInfo, userList
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 {
inboundInfo := value.(*InboundInfo)
// Update User info
for _, u := range *usersIndex {
/*if (*updatedUserList)[u].SpeedLimit == 0 {
(*updatedUserList)[u].SpeedLimit = nodeInfo.SpeedLimit
}
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,
for i := range *updatedUserList {
inboundInfo.UserInfo.Store(fmt.Sprintf("%s|%s|%d", tag,
(*updatedUserList)[i].V2rayUser.Email, (*updatedUserList)[i].UID), UserInfo{
UID: (*updatedUserList)[i].UID,
SpeedLimit: nodeInfo.SpeedLimit,
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 {
return fmt.Errorf("no such inbound in limiter: %s", tag)
@@ -90,34 +85,78 @@ func (l *Limiter) DeleteInboundLimiter(tag string) error {
return nil
}
func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
onlineUser := make([]api.OnlineUser, 0)
type UserIp struct {
Uid int `json:"Uid"`
IPs []string `json:"Ips"`
}
func (l *Limiter) GetOnlineUserIp(tag string) (*[]UserIp, error) {
if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo)
// Clear Speed Limiter bucket for users who are not online
inboundInfo.BucketHub.Range(func(key, value interface{}) bool {
email := key.(string)
if _, exists := inboundInfo.UserOnlineIP.Load(email); !exists {
inboundInfo.BucketHub.Delete(email)
if _, exists := inboundInfo.UserOnlineIP.Load(key.(string)); !exists {
inboundInfo.BucketHub.Delete(key.(string))
}
return true
})
onlineUser := make([]UserIp, 0)
var ipMap *sync.Map
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
ipMap := value.(*sync.Map)
ipMap.Range(func(key, value interface{}) bool {
ip := key.(string)
uid := value.(int)
onlineUser = append(onlineUser, api.OnlineUser{UID: uid, IP: ip})
ipMap = value.(*sync.Map)
var ip []string
ipMap.Range(func(key, v interface{}) bool {
if v.(bool) {
ip = append(ip, key.(string))
}
return true
})
email := key.(string)
inboundInfo.UserOnlineIP.Delete(email) // Reset online device
if len(ip) > 0 {
if u, ok := inboundInfo.UserInfo.Load(key.(string)); ok {
onlineUser = append(onlineUser, UserIp{
Uid: u.(UserInfo).UID,
IPs: ip,
})
}
}
return true
})
if len(onlineUser) == 0 {
return nil, nil
}
return &onlineUser, nil
} else {
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) {
@@ -126,21 +165,18 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
nodeLimit := inboundInfo.NodeSpeedLimit
var userLimit uint64 = 0
var deviceLimit = 0
var uid = 0
if v, ok := inboundInfo.UserInfo.Load(email); ok {
u := v.(UserInfo)
uid = u.UID
userLimit = u.SpeedLimit
deviceLimit = u.DeviceLimit
}
// Report online device
ipMap := new(sync.Map)
ipMap.Store(ip, uid)
ipMap.Store(ip, true)
// If any device is online
if v, ok := inboundInfo.UserOnlineIP.LoadOrStore(email, ipMap); ok {
ipMap := v.(*sync.Map)
// If this ip is a new device
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
if online, ok := ipMap.LoadOrStore(ip, true); !ok {
counter := 0
ipMap.Range(func(key, value interface{}) bool {
counter++
@@ -150,6 +186,10 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
ipMap.Delete(ip)
return nil, false, true
}
} else {
if !online.(bool) {
ipMap.Store(ip, true)
}
}
}
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
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"`
}
package conf
type CertConfig struct {
CertMode string `mapstructure:"CertMode"` // none, file, http, dns
@@ -34,3 +18,49 @@ type FallBackConfig struct {
Dest string `mapstructure:"Dest"`
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
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
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:
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
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 (
"flag"
"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"
"os"
"os/signal"
@@ -10,11 +15,6 @@ import (
"runtime"
"strings"
"syscall"
"time"
"github.com/Yuzuki616/V2bX/panel"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
var (
@@ -23,7 +23,7 @@ var (
)
var (
version = "v0.0.2"
version = "v0.0.4"
codename = "V2bX"
intro = "A V2board backend based on Xray"
)
@@ -34,7 +34,6 @@ func showVersion() {
func getConfig() *viper.Viper {
config := viper.New()
// Set custom path and name
if *configFile != "" {
configName := path.Base(*configFile)
@@ -52,46 +51,44 @@ func getConfig() *viper.Viper {
config.SetConfigName("config")
config.SetConfigType("yml")
config.AddConfigPath(".")
}
if err := config.ReadInConfig(); err != nil {
log.Panicf("Fatal error config file: %s \n", err)
}
config.WatchConfig() // Watch the 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() {
flag.Parse()
showVersion()
if *printVersion {
return
}
config := getConfig()
panelConfig := &panel.Config{}
config.Unmarshal(panelConfig)
p := panel.New(panelConfig)
lastTime := time.Now()
config.OnConfigChange(func(e fsnotify.Event) {
// Discarding event received within a short period of time after receiving an event.
if time.Now().After(lastTime.Add(3 * time.Second)) {
// Hot reload function
fmt.Println("Config file changed:", e.Name)
p.Close()
// Delete old instance and trigger GC
runtime.GC()
config.Unmarshal(panelConfig)
p.Start()
lastTime = time.Now()
}
})
p.Start()
defer p.Close()
c := conf.New()
err := config.Unmarshal(c)
if err != nil {
log.Panicf("can't unmarshal config file: %s \n", err)
}
x := xray.New(c)
x.Start()
defer x.Close()
err = startNodes(c.NodesConfig, x)
if err != nil {
log.Panicf("run nodes error: %v", err)
}
//Explicitly triggering GC to remove garbage from config loading.
runtime.GC()
// Running backend
@@ -1,19 +1,20 @@
// Package controller the InbounderConfig used by add inbound
package controller
// Package node the InbounderConfig used by add inbound
package node
import (
"encoding/json"
"fmt"
"github.com/Yuzuki616/V2bX/api"
"github.com/Yuzuki616/V2bX/common/legoCmd"
"github.com/Yuzuki616/V2bX/conf"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/uuid"
"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
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{}
if nodeInfo.NodeType == "V2ray" {
defer func() {
@@ -25,7 +26,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
if config.EnableFallback {
fallbackConfigs, err := buildVlessFallbacks(config.FallBackConfigs)
if err == nil {
proxySetting = &conf.VLessInboundConfig{
proxySetting = &coreConf.VLessInboundConfig{
Decryption: "none",
Fallbacks: fallbackConfigs,
}
@@ -33,13 +34,13 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
return nil, err
}
} else {
proxySetting = &conf.VLessInboundConfig{
proxySetting = &coreConf.VLessInboundConfig{
Decryption: "none",
}
}
} else {
nodeInfo.V2ray.Inbounds[0].Protocol = "vmess"
proxySetting = &conf.VMessInboundConfig{}
proxySetting = &coreConf.VMessInboundConfig{}
}
} else if nodeInfo.NodeType == "Trojan" {
defer func() {
@@ -47,55 +48,55 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
nodeInfo.Trojan = nil
}()
nodeInfo.V2ray = &api.V2rayConfig{}
nodeInfo.V2ray.Inbounds = make([]conf.InboundDetourConfig, 1)
nodeInfo.V2ray.Inbounds = make([]coreConf.InboundDetourConfig, 1)
nodeInfo.V2ray.Inbounds[0].Protocol = "trojan"
// Enable fallback
if config.EnableFallback {
fallbackConfigs, err := buildTrojanFallbacks(config.FallBackConfigs)
if err == nil {
proxySetting = &conf.TrojanServerConfig{
proxySetting = &coreConf.TrojanServerConfig{
Fallbacks: fallbackConfigs,
}
} else {
return nil, err
}
} else {
proxySetting = &conf.TrojanServerConfig{}
proxySetting = &coreConf.TrojanServerConfig{}
}
nodeInfo.V2ray.Inbounds[0].PortList = &conf.PortList{
Range: []conf.PortRange{{From: uint32(nodeInfo.Trojan.LocalPort), To: uint32(nodeInfo.Trojan.LocalPort)}},
nodeInfo.V2ray.Inbounds[0].PortList = &coreConf.PortList{
Range: []coreConf.PortRange{{From: uint32(nodeInfo.Trojan.LocalPort), To: uint32(nodeInfo.Trojan.LocalPort)}},
}
t := conf.TransportProtocol(nodeInfo.Trojan.TransportProtocol)
nodeInfo.V2ray.Inbounds[0].StreamSetting = &conf.StreamConfig{Network: &t}
t := coreConf.TransportProtocol(nodeInfo.Trojan.TransportProtocol)
nodeInfo.V2ray.Inbounds[0].StreamSetting = &coreConf.StreamConfig{Network: &t}
} else if nodeInfo.NodeType == "Shadowsocks" {
defer func() {
nodeInfo.V2ray = nil
}()
nodeInfo.V2ray = &api.V2rayConfig{}
nodeInfo.V2ray.Inbounds = []conf.InboundDetourConfig{{Protocol: "shadowsocks"}}
proxySetting = &conf.ShadowsocksServerConfig{}
nodeInfo.V2ray.Inbounds = []coreConf.InboundDetourConfig{{Protocol: "shadowsocks"}}
proxySetting = &coreConf.ShadowsocksServerConfig{}
randomPasswd := uuid.New()
defaultSSuser := &conf.ShadowsocksUserConfig{
defaultSSuser := &coreConf.ShadowsocksUserConfig{
Cipher: "aes-128-gcm",
Password: randomPasswd.String(),
}
proxySetting, _ := proxySetting.(*conf.ShadowsocksServerConfig)
proxySetting, _ := proxySetting.(*coreConf.ShadowsocksServerConfig)
proxySetting.Users = append(proxySetting.Users, defaultSSuser)
proxySetting.NetworkList = &conf.NetworkList{"tcp", "udp"}
proxySetting.NetworkList = &coreConf.NetworkList{"tcp", "udp"}
proxySetting.IVCheck = true
if config.DisableIVCheck {
proxySetting.IVCheck = false
}
nodeInfo.V2ray.Inbounds[0].PortList = &conf.PortList{
Range: []conf.PortRange{{From: uint32(nodeInfo.SS.Port), To: uint32(nodeInfo.SS.Port)}},
nodeInfo.V2ray.Inbounds[0].PortList = &coreConf.PortList{
Range: []coreConf.PortRange{{From: uint32(nodeInfo.SS.Port), To: uint32(nodeInfo.SS.Port)}},
}
t := conf.TransportProtocol(nodeInfo.SS.TransportProtocol)
nodeInfo.V2ray.Inbounds[0].StreamSetting = &conf.StreamConfig{Network: &t}
t := coreConf.TransportProtocol(nodeInfo.SS.TransportProtocol)
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([]conf.InboundDetourConfig, 1)
nodeInfo.V2ray.Inbounds = make([]coreConf.InboundDetourConfig, 1)
nodeInfo.V2ray.Inbounds[0].Protocol = "dokodemo-door"
proxySetting = struct {
Host string `json:"address"`
@@ -107,11 +108,11 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
}*/
// Build Listen IP address
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 := &conf.SniffingConfig{
sniffingConfig := &coreConf.SniffingConfig{
Enabled: true,
DestOverride: &conf.StringList{"http", "tls"},
DestOverride: &coreConf.StringList{"http", "tls"},
}
if config.DisableSniffing {
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)
}
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
} else {
tcpSetting := &conf.TCPConfig{
tcpSetting := &coreConf.TCPConfig{
AcceptProxyProtocol: config.EnableProxyProtocol,
}
nodeInfo.V2ray.Inbounds[0].StreamSetting.TCPSettings = tcpSetting
}
} 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}
}
// Build TLS and XTLS settings
@@ -147,17 +148,17 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
return nil, err
}
if nodeInfo.TLSType == "tls" {
tlsSettings := &conf.TLSConfig{
tlsSettings := &coreConf.TLSConfig{
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
} else if nodeInfo.TLSType == "xtls" {
xtlsSettings := &conf.XTLSConfig{
xtlsSettings := &coreConf.XTLSConfig{
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
}
xtlsSettings.Certs = append(xtlsSettings.Certs, &conf.XTLSCertConfig{
xtlsSettings.Certs = append(xtlsSettings.Certs, &coreConf.XTLSCertConfig{
CertFile: certFile,
KeyFile: keyFile,
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" &&
*nodeInfo.V2ray.Inbounds[0].StreamSetting.Network != "ws" &&
config.EnableProxyProtocol {
sockoptConfig := &conf.SocketConfig{
sockoptConfig := &coreConf.SocketConfig{
AcceptProxyProtocol: config.EnableProxyProtocol,
}
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()
}
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.CertFile == "" || certConfig.KeyFile == "" {
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)
}
func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboundFallback, error) {
func buildVlessFallbacks(fallbackConfigs []*conf.FallBackConfig) ([]*coreConf.VLessInboundFallback, error) {
if fallbackConfigs == nil {
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 {
if c.Dest == "" {
@@ -226,7 +227,7 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
if err != nil {
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
}
vlessFallBacks[i] = &conf.VLessInboundFallback{
vlessFallBacks[i] = &coreConf.VLessInboundFallback{
Name: c.SNI,
Alpn: c.Alpn,
Path: c.Path,
@@ -237,12 +238,12 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
return vlessFallBacks, nil
}
func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInboundFallback, error) {
func buildTrojanFallbacks(fallbackConfigs []*conf.FallBackConfig) ([]*coreConf.TrojanInboundFallback, error) {
if fallbackConfigs == nil {
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 {
if c.Dest == "" {
@@ -254,7 +255,7 @@ func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInbo
if err != nil {
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
}
trojanFallBacks[i] = &conf.TrojanInboundFallback{
trojanFallBacks[i] = &coreConf.TrojanInboundFallback{
Name: c.SNI,
Alpn: c.Alpn,
Path: c.Path,
@@ -1,10 +1,10 @@
package controller_test
package node_test
import (
. "github.com/Yuzuki616/V2bX/node"
"testing"
"github.com/Yuzuki616/V2bX/api"
. "github.com/Yuzuki616/V2bX/service/controller"
)
func TestBuildV2ray(t *testing.T) {
+102 -89
View File
@@ -1,7 +1,12 @@
package controller
package node
import (
"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"
"math"
"reflect"
@@ -12,12 +17,11 @@ import (
"github.com/Yuzuki616/V2bX/common/legoCmd"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
)
type Controller struct {
server *core.Instance
config *Config
type Node struct {
server *xray.Xray
config *conf.ControllerConfig
clientInfo api.ClientInfo
apiClient api.API
nodeInfo *api.NodeInfo
@@ -25,12 +29,12 @@ type Controller struct {
userList *[]api.UserInfo
nodeInfoMonitorPeriodic *task.Periodic
userReportPeriodic *task.Periodic
panelType string
onlineIpReportPeriodic *task.Periodic
}
// New return a Controller service with default parameters.
func New(server *core.Instance, api api.API, config *Config) *Controller {
controller := &Controller{
// New return a Node service with default parameters.
func New(server *xray.Xray, api api.API, config *conf.ControllerConfig) *Node {
controller := &Node{
server: server,
config: config,
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
func (c *Controller) Start() error {
func (c *Node) Start() error {
c.clientInfo = c.apiClient.Describe()
// First fetch Node Info
newNodeInfo, err := c.apiClient.GetNodeInfo()
@@ -66,7 +70,7 @@ func (c *Controller) Start() error {
}
//sync controller userList
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)
}
// Add Rule Manager
@@ -74,11 +78,11 @@ func (c *Controller) Start() error {
if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil {
log.Printf("Get rule list filed: %s", err)
} 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)
}
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)
}
}
@@ -105,12 +109,24 @@ func (c *Controller) Start() error {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = 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()
return nil
}
// Close implement the Close() function of the service interface
func (c *Controller) Close() error {
func (c *Node) Close() error {
if c.nodeInfoMonitorPeriodic != nil {
err := c.nodeInfoMonitorPeriodic.Close()
if err != nil {
@@ -124,10 +140,16 @@ func (c *Controller) Close() error {
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
}
func (c *Controller) nodeInfoMonitor() (err error) {
func (c *Node) nodeInfoMonitor() (err error) {
// First fetch Node Info
newNodeInfo, err := c.apiClient.GetNodeInfo()
if err != nil {
@@ -156,7 +178,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
}
nodeInfoChanged = true
// Remove Old limiter
if err = c.DeleteInboundLimiter(oldtag); err != nil {
if err = c.server.DeleteInboundLimiter(oldtag); err != nil {
log.Print(err)
return nil
}
@@ -168,11 +190,11 @@ func (c *Controller) nodeInfoMonitor() (err error) {
if ruleList, protocolRule, err := c.apiClient.GetNodeRule(); err != nil {
log.Printf("Get rule list filed: %s", err)
} 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)
}
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)
}
}
@@ -210,37 +232,37 @@ func (c *Controller) nodeInfoMonitor() (err error) {
}
newNodeInfo = nil
// 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)
return nil
}
runtime.GC()
} else {
deleted, added := compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i := range deleted {
if len(*deleted) > 0 {
deletedEmail := make([]string, len(*deleted))
for i := range *deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag,
(*c.userList)[deleted[i]].GetUserEmail(),
(*c.userList)[deleted[i]].UID)
(*deleted)[i].GetUserEmail(),
(*deleted)[i].UID)
}
err := c.removeUsers(deletedEmail, c.Tag)
err := c.server.RemoveUsers(deletedEmail, c.Tag)
if err != nil {
log.Print(err)
}
}
if len(added) > 0 {
err = c.addNewUserFromIndex(newUserInfo, &added, c.nodeInfo)
if len(*added) > 0 {
err = c.addNewUser(added, newNodeInfo)
if err != nil {
log.Print(err)
}
// 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.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
newUserInfo = nil
runtime.GC()
@@ -248,24 +270,24 @@ func (c *Controller) nodeInfoMonitor() (err error) {
return nil
}
func (c *Controller) removeOldTag(oldtag string) (err error) {
err = c.removeInbound(oldtag)
func (c *Node) removeOldTag(oldtag string) (err error) {
err = c.server.RemoveInbound(oldtag)
if err != nil {
return err
}
err = c.removeOutbound(oldtag)
err = c.server.RemoveOutbound(oldtag)
if err != nil {
return err
}
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)
if err != nil {
return err
}
err = c.addInbound(inboundConfig)
err = c.server.AddInbound(inboundConfig)
if err != nil {
return err
@@ -275,7 +297,7 @@ func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
return err
}
err = c.addOutbound(outBoundConfig)
err = c.server.AddOutbound(outBoundConfig)
if err != nil {
return err
@@ -283,7 +305,7 @@ func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
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)
if nodeInfo.NodeType == "V2ray" {
if nodeInfo.EnableVless {
@@ -301,11 +323,11 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
} else if nodeInfo.NodeType == "Trojan" {
users = c.buildTrojanUsers(userInfo)
} else if nodeInfo.NodeType == "Shadowsocks" {
users = c.buildSSUsers(userInfo, nodeInfo.SS.CypherMethod)
users = c.buildSSUsers(userInfo, getCipherFromString(nodeInfo.SS.CypherMethod))
} else {
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 {
return err
}
@@ -313,39 +335,7 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
return nil
}
func (c *Controller) addNewUserFromIndex(userInfo *[]api.UserInfo, userIndex *[]int, nodeInfo *api.NodeInfo) (err error) {
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) {
func compareUserList(old, new *[]api.UserInfo) (deleted, added *[]api.UserInfo) {
tmp := map[string]struct{}{}
tmp2 := map[string]struct{}{}
for i := range *old {
@@ -357,7 +347,7 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []int) {
tmp[e] = struct{}{}
tmp2[e] = struct{}{}
if l != len(tmp) {
added = append(added, i)
*added = append(*added, (*new)[i])
l++
}
}
@@ -366,18 +356,18 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []int) {
for i := range *old {
tmp2[(*old)[i].GetUserEmail()] = struct{}{}
if l != len(tmp2) {
deleted = append(deleted, i)
*deleted = append(*deleted, (*old)[i])
l++
}
}
return deleted, added
}
func (c *Controller) userInfoMonitor() (err error) {
func (c *Node) userInfoMonitor() (err error) {
// Get User traffic
userTraffic := make([]api.UserTraffic, 0)
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 {
userTraffic = append(userTraffic, api.UserTraffic{
UID: (*c.userList)[i].UID,
@@ -389,26 +379,49 @@ func (c *Controller) userInfoMonitor() (err error) {
err = c.apiClient.ReportUserTraffic(&userTraffic)
if err != nil {
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
runtime.GC()
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)
}
@@ -1,7 +1,8 @@
package controller_test
package node_test
import (
"fmt"
. "github.com/Yuzuki616/V2bX/node"
"os"
"os/signal"
"runtime"
@@ -9,8 +10,7 @@ import (
"testing"
"github.com/Yuzuki616/V2bX/api"
_ "github.com/Yuzuki616/V2bX/main/distro/all"
. "github.com/Yuzuki616/V2bX/service/controller"
_ "github.com/Yuzuki616/V2bX/example/distro/all"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
)
@@ -1,8 +1,9 @@
package controller
package node
import (
"encoding/json"
"fmt"
conf2 "github.com/Yuzuki616/V2bX/conf"
"github.com/Yuzuki616/V2bX/api"
"github.com/xtls/xray-core/common/net"
@@ -11,7 +12,7 @@ import (
)
//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.Protocol = "freedom"
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.
// _ "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/outbound"
@@ -29,13 +29,6 @@ import (
_ "github.com/xtls/xray-core/proxy/dns"
_ "github.com/xtls/xray-core/proxy/dokodemo"
_ "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/outbound"
@@ -66,7 +59,4 @@ import (
// Load config from file or http(s)
_ "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
}