From 96493346f9c20603b771245bb547a3ff34f26e42 Mon Sep 17 00:00:00 2001 From: Yuzuki616 Date: Sat, 29 Jul 2023 18:47:47 +0800 Subject: [PATCH] update sing-box support --- api/panel/node_test.go | 3 +- common/counter/conn.go | 93 +++++++++++++++++++++++--- common/counter/traffic.go | 6 ++ common/rate/conn.go | 56 ++++++++++++++++ common/rate/{rate.go => writer.go} | 0 conf/cert.go | 24 +++++++ conf/conf.go | 21 ++---- conf/conf_test.go | 8 +-- conf/core.go | 29 +-------- conf/limit.go | 40 ++++++++++++ conf/log.go | 15 ----- conf/node.go | 101 +---------------------------- conf/old.go | 81 ----------------------- conf/option.go | 36 ++++++++++ conf/sing.go | 22 +++++++ conf/xray.go | 47 ++++++++++++++ core/hy/node.go | 2 +- core/hy/server.go | 2 +- core/hy/server_test.go | 4 +- core/interface.go | 4 +- core/selector.go | 2 +- core/sing/hook.go | 36 ++++++++-- core/sing/node.go | 26 ++++++-- core/sing/sing.go | 8 ++- core/sing/user.go | 4 +- core/xray/inbound.go | 8 +-- core/xray/node.go | 2 +- core/xray/outbound.go | 2 +- core/xray/xray.go | 2 +- go.mod | 2 +- go.sum | 6 +- node/controller.go | 14 ++-- node/node.go | 3 +- node/task.go | 6 +- 34 files changed, 418 insertions(+), 297 deletions(-) create mode 100644 common/rate/conn.go rename common/rate/{rate.go => writer.go} (100%) create mode 100644 conf/cert.go create mode 100644 conf/limit.go delete mode 100644 conf/log.go delete mode 100644 conf/old.go create mode 100644 conf/option.go create mode 100644 conf/sing.go create mode 100644 conf/xray.go diff --git a/api/panel/node_test.go b/api/panel/node_test.go index 570182f..e340fb0 100644 --- a/api/panel/node_test.go +++ b/api/panel/node_test.go @@ -1,9 +1,10 @@ package panel import ( - "github.com/Yuzuki616/V2bX/conf" "log" "testing" + + "github.com/Yuzuki616/V2bX/conf" ) var client *Client diff --git a/common/counter/conn.go b/common/counter/conn.go index cb5ee6c..8f9db03 100644 --- a/common/counter/conn.go +++ b/common/counter/conn.go @@ -1,8 +1,11 @@ package counter import ( + "io" "net" + "github.com/sagernet/sing/common/bufio" + "github.com/sagernet/sing/common/buf" M "github.com/sagernet/sing/common/metadata" @@ -10,38 +13,93 @@ import ( ) type ConnCounter struct { - net.Conn - storage *TrafficStorage + network.ExtendedConn + storage *TrafficStorage + readFunc network.CountFunc + writeFunc network.CountFunc } func NewConnCounter(conn net.Conn, s *TrafficStorage) net.Conn { return &ConnCounter{ - Conn: conn, - storage: s, + ExtendedConn: bufio.NewExtendedConn(conn), + storage: s, + readFunc: func(n int64) { + s.DownCounter.Add(n) + }, + writeFunc: func(n int64) { + s.UpCounter.Add(n) + }, } } func (c *ConnCounter) Read(b []byte) (n int, err error) { - n, err = c.Conn.Read(b) + n, err = c.ExtendedConn.Read(b) c.storage.DownCounter.Store(int64(n)) return } func (c *ConnCounter) Write(b []byte) (n int, err error) { - n, err = c.Conn.Write(b) + n, err = c.ExtendedConn.Write(b) c.storage.UpCounter.Store(int64(n)) return } +func (c *ConnCounter) ReadBuffer(buffer *buf.Buffer) error { + err := c.ExtendedConn.ReadBuffer(buffer) + if err != nil { + return err + } + if buffer.Len() > 0 { + c.storage.DownCounter.Add(int64(buffer.Len())) + } + return nil +} + +func (c *ConnCounter) WriteBuffer(buffer *buf.Buffer) error { + dataLen := int64(buffer.Len()) + err := c.ExtendedConn.WriteBuffer(buffer) + if err != nil { + return err + } + if dataLen > 0 { + c.storage.UpCounter.Add(dataLen) + } + return nil +} + +func (c *ConnCounter) UnwrapReader() (io.Reader, []network.CountFunc) { + return c.ExtendedConn, []network.CountFunc{ + c.readFunc, + } +} + +func (c *ConnCounter) UnwrapWriter() (io.Writer, []network.CountFunc) { + return c.ExtendedConn, []network.CountFunc{ + c.writeFunc, + } +} + +func (c *ConnCounter) Upstream() any { + return c.ExtendedConn +} + type PacketConnCounter struct { network.PacketConn - storage *TrafficStorage + storage *TrafficStorage + readFunc network.CountFunc + writeFunc network.CountFunc } func NewPacketConnCounter(conn network.PacketConn, s *TrafficStorage) network.PacketConn { return &PacketConnCounter{ PacketConn: conn, storage: s, + readFunc: func(n int64) { + s.DownCounter.Add(n) + }, + writeFunc: func(n int64) { + s.UpCounter.Add(n) + }, } } @@ -55,10 +113,29 @@ func (p *PacketConnCounter) ReadPacket(buff *buf.Buffer) (destination M.Socksadd } func (p *PacketConnCounter) WritePacket(buff *buf.Buffer, destination M.Socksaddr) (err error) { + n := buff.Len() err = p.PacketConn.WritePacket(buff, destination) if err != nil { return } - p.storage.UpCounter.Add(int64(buff.Len())) + if n > 0 { + p.storage.UpCounter.Add(int64(n)) + } return } + +func (p *PacketConnCounter) UnwrapPacketReader() (network.PacketReader, []network.CountFunc) { + return p.PacketConn, []network.CountFunc{ + p.readFunc, + } +} + +func (p *PacketConnCounter) UnwrapPacketWriter() (network.PacketWriter, []network.CountFunc) { + return p.PacketConn, []network.CountFunc{ + p.writeFunc, + } +} + +func (p *PacketConnCounter) Upstream() any { + return p.PacketConn +} diff --git a/common/counter/traffic.go b/common/counter/traffic.go index 3e477dd..2af0ae4 100644 --- a/common/counter/traffic.go +++ b/common/counter/traffic.go @@ -52,6 +52,12 @@ func (c *TrafficCounter) GetDownCount(id string) int64 { return 0 } +func (c *TrafficCounter) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + return len(c.counters) +} + func (c *TrafficCounter) Reset(id string) { c.lock.RLock() cts := c.GetCounter(id) diff --git a/common/rate/conn.go b/common/rate/conn.go new file mode 100644 index 0000000..f0b99c2 --- /dev/null +++ b/common/rate/conn.go @@ -0,0 +1,56 @@ +package rate + +import ( + "net" + + "github.com/juju/ratelimit" + "github.com/sagernet/sing/common/buf" + M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/network" +) + +func NewConnRateLimiter(c net.Conn, l *ratelimit.Bucket) *Conn { + return &Conn{ + Conn: c, + limiter: l, + } +} + +type Conn struct { + net.Conn + limiter *ratelimit.Bucket +} + +func (c *Conn) Read(b []byte) (n int, err error) { + c.limiter.Wait(int64(len(b))) + return c.Conn.Read(b) +} + +func (c *Conn) Write(b []byte) (n int, err error) { + c.limiter.Wait(int64(len(b))) + return c.Conn.Write(b) +} + +type PacketConnCounter struct { + network.PacketConn + limiter *ratelimit.Bucket +} + +func NewPacketConnCounter(conn network.PacketConn, l *ratelimit.Bucket) network.PacketConn { + return &PacketConnCounter{ + PacketConn: conn, + limiter: l, + } +} + +func (p *PacketConnCounter) ReadPacket(buff *buf.Buffer) (destination M.Socksaddr, err error) { + pLen := buff.Len() + destination, err = p.ReadPacket(buff) + p.limiter.Wait(int64(buff.Len() - pLen)) + return +} + +func (p *PacketConnCounter) WritePacket(buff *buf.Buffer, destination M.Socksaddr) (err error) { + p.limiter.Wait(int64(buff.Len())) + return p.PacketConn.WritePacket(buff, destination) +} diff --git a/common/rate/rate.go b/common/rate/writer.go similarity index 100% rename from common/rate/rate.go rename to common/rate/writer.go diff --git a/conf/cert.go b/conf/cert.go new file mode 100644 index 0000000..a921964 --- /dev/null +++ b/conf/cert.go @@ -0,0 +1,24 @@ +package conf + +type CertConfig struct { + CertMode string `yaml:"CertMode"` // none, file, http, dns + RejectUnknownSni bool `yaml:"RejectUnknownSni"` + CertDomain string `yaml:"CertDomain"` + CertFile string `yaml:"CertFile"` + KeyFile string `yaml:"KeyFile"` + Provider string `yaml:"Provider"` // alidns, cloudflare, gandi, godaddy.... + Email string `yaml:"Email"` + DNSEnv map[string]string `yaml:"DNSEnv"` + RealityConfig *RealityConfig `yaml:"RealityConfig"` +} + +type RealityConfig struct { + Dest interface{} `yaml:"Dest" json:"Dest"` + Xver uint64 `yaml:"Xver" json:"Xver"` + ServerNames []string `yaml:"ServerNames" json:"ServerNames"` + PrivateKey string `yaml:"PrivateKey" json:"PrivateKey"` + MinClientVer string `yaml:"MinClientVer" json:"MinClientVer"` + MaxClientVer string `yaml:"MaxClientVer" json:"MaxClientVer"` + MaxTimeDiff uint64 `yaml:"MaxTimeDiff" json:"MaxTimeDiff"` + ShortIds []string `yaml:"ShortIds" json:"ShortIds"` +} diff --git a/conf/conf.go b/conf/conf.go index 4f2c7e8..b466bea 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -2,9 +2,10 @@ package conf import ( "fmt" - "gopkg.in/yaml.v3" "io" "os" + + "gopkg.in/yaml.v3" ) type Conf struct { @@ -15,16 +16,9 @@ type Conf struct { func New() *Conf { return &Conf{ CoreConfig: CoreConfig{ - Type: "xray", - XrayConfig: &XrayConfig{ - LogConfig: NewLogConfig(), - AssetPath: "/etc/V2bX/", - DnsConfigPath: "", - InboundConfigPath: "", - OutboundConfigPath: "", - RouteConfigPath: "", - ConnectionConfig: NewConnectionConfig(), - }, + Type: "xray", + XrayConfig: NewXrayConfig(), + SingConfig: NewSingConfig(), }, NodesConfig: []*NodeConfig{}, } @@ -44,10 +38,5 @@ func (p *Conf) LoadFromPath(filePath string) error { if err != nil { return fmt.Errorf("decode config error: %s", err) } - old := &OldConfig{} - err = yaml.Unmarshal(content, old) - if err == nil { - migrateOldConfig(p, old) - } return nil } diff --git a/conf/conf_test.go b/conf/conf_test.go index 44bc756..c466a6c 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -2,6 +2,7 @@ package conf import ( "log" + "strings" "testing" ) @@ -11,9 +12,6 @@ func TestConf_LoadFromPath(t *testing.T) { } func TestConf_Watch(t *testing.T) { - c := New() - log.Println(c.Watch("../example/config.yml.example", func() { - log.Println(1) - })) - select {} + //c := New() + log.Println(strings.Split("aaaa", " ")) } diff --git a/conf/core.go b/conf/core.go index 45b8ce2..625a28b 100644 --- a/conf/core.go +++ b/conf/core.go @@ -3,32 +3,5 @@ package conf type CoreConfig struct { Type string `yaml:"Type"` XrayConfig *XrayConfig `yaml:"XrayConfig"` -} - -type XrayConfig struct { - LogConfig *LogConfig `yaml:"Log"` - AssetPath string `yaml:"AssetPath"` - DnsConfigPath string `yaml:"DnsConfigPath"` - RouteConfigPath string `yaml:"RouteConfigPath"` - ConnectionConfig *ConnectionConfig `yaml:"ConnectionConfig"` - InboundConfigPath string `yaml:"InboundConfigPath"` - OutboundConfigPath string `yaml:"OutboundConfigPath"` -} - -type ConnectionConfig struct { - Handshake uint32 `yaml:"handshake"` - ConnIdle uint32 `yaml:"connIdle"` - UplinkOnly uint32 `yaml:"uplinkOnly"` - DownlinkOnly uint32 `yaml:"downlinkOnly"` - BufferSize int32 `yaml:"bufferSize"` -} - -func NewConnectionConfig() *ConnectionConfig { - return &ConnectionConfig{ - Handshake: 4, - ConnIdle: 30, - UplinkOnly: 2, - DownlinkOnly: 4, - BufferSize: 64, - } + SingConfig *SingConfig `yaml:"SingConfig"` } diff --git a/conf/limit.go b/conf/limit.go new file mode 100644 index 0000000..b4ce6e3 --- /dev/null +++ b/conf/limit.go @@ -0,0 +1,40 @@ +package conf + +type LimitConfig struct { + EnableRealtime bool `yaml:"EnableRealtime"` + SpeedLimit int `yaml:"SpeedLimit"` + IPLimit int `yaml:"DeviceLimit"` + ConnLimit int `yaml:"ConnLimit"` + EnableIpRecorder bool `yaml:"EnableIpRecorder"` + IpRecorderConfig *IpReportConfig `yaml:"IpRecorderConfig"` + EnableDynamicSpeedLimit bool `yaml:"EnableDynamicSpeedLimit"` + DynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:"DynamicSpeedLimitConfig"` +} + +type RecorderConfig struct { + Url string `yaml:"Url"` + Token string `yaml:"Token"` + Timeout int `yaml:"Timeout"` +} + +type RedisConfig struct { + Address string `yaml:"Address"` + Password string `yaml:"Password"` + Db int `yaml:"Db"` + Expiry int `json:"Expiry"` +} + +type IpReportConfig struct { + Periodic int `yaml:"Periodic"` + Type string `yaml:"Type"` + RecorderConfig *RecorderConfig `yaml:"RecorderConfig"` + RedisConfig *RedisConfig `yaml:"RedisConfig"` + EnableIpSync bool `yaml:"EnableIpSync"` +} + +type DynamicSpeedLimitConfig struct { + Periodic int `yaml:"Periodic"` + Traffic int64 `yaml:"Traffic"` + SpeedLimit int `yaml:"SpeedLimit"` + ExpireTime int `yaml:"ExpireTime"` +} diff --git a/conf/log.go b/conf/log.go deleted file mode 100644 index 86bb6d1..0000000 --- a/conf/log.go +++ /dev/null @@ -1,15 +0,0 @@ -package conf - -type LogConfig struct { - Level string `yaml:"Level"` - AccessPath string `yaml:"AccessPath"` - ErrorPath string `yaml:"ErrorPath"` -} - -func NewLogConfig() *LogConfig { - return &LogConfig{ - Level: "warning", - AccessPath: "", - ErrorPath: "", - } -} diff --git a/conf/node.go b/conf/node.go index 868bd83..365f20b 100644 --- a/conf/node.go +++ b/conf/node.go @@ -1,8 +1,8 @@ package conf type NodeConfig struct { - ApiConfig *ApiConfig `yaml:"ApiConfig"` - ControllerConfig *ControllerConfig `yaml:"ControllerConfig"` + ApiConfig *ApiConfig `yaml:"ApiConfig"` + Options *Options `yaml:"Options"` } type ApiConfig struct { @@ -13,100 +13,3 @@ type ApiConfig struct { Timeout int `yaml:"Timeout"` RuleListPath string `yaml:"RuleListPath"` } - -type ControllerConfig struct { - ListenIP string `yaml:"ListenIP"` - SendIP string `yaml:"SendIP"` - XrayOptions XrayOptions `yaml:"XrayOptions"` - HyOptions HyOptions `yaml:"HyOptions"` - LimitConfig LimitConfig `yaml:"LimitConfig"` - CertConfig *CertConfig `yaml:"CertConfig"` -} - -type RealityConfig struct { - Dest interface{} `yaml:"Dest" json:"Dest"` - Xver uint64 `yaml:"Xver" json:"Xver"` - ServerNames []string `yaml:"ServerNames" json:"ServerNames"` - PrivateKey string `yaml:"PrivateKey" json:"PrivateKey"` - MinClientVer string `yaml:"MinClientVer" json:"MinClientVer"` - MaxClientVer string `yaml:"MaxClientVer" json:"MaxClientVer"` - MaxTimeDiff uint64 `yaml:"MaxTimeDiff" json:"MaxTimeDiff"` - ShortIds []string `yaml:"ShortIds" json:"ShortIds"` -} - -type XrayOptions struct { - EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` - EnableDNS bool `yaml:"EnableDNS"` - DNSType string `yaml:"DNSType"` - EnableUot bool `yaml:"EnableUot"` - EnableTFO bool `yaml:"EnableTFO"` - DisableIVCheck bool `yaml:"DisableIVCheck"` - DisableSniffing bool `yaml:"DisableSniffing"` - EnableFallback bool `yaml:"EnableFallback"` - FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"` -} - -type HyOptions struct { - Resolver string `yaml:"Resolver"` - ResolvePreference string `yaml:"ResolvePreference"` - SendDevice string `yaml:"SendDevice"` -} - -type LimitConfig struct { - EnableRealtime bool `yaml:"EnableRealtime"` - SpeedLimit int `yaml:"SpeedLimit"` - IPLimit int `yaml:"DeviceLimit"` - ConnLimit int `yaml:"ConnLimit"` - EnableIpRecorder bool `yaml:"EnableIpRecorder"` - IpRecorderConfig *IpReportConfig `yaml:"IpRecorderConfig"` - EnableDynamicSpeedLimit bool `yaml:"EnableDynamicSpeedLimit"` - DynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:"DynamicSpeedLimitConfig"` -} - -type FallBackConfig struct { - SNI string `yaml:"SNI"` - Alpn string `yaml:"Alpn"` - Path string `yaml:"Path"` - Dest string `yaml:"Dest"` - ProxyProtocolVer uint64 `yaml:"ProxyProtocolVer"` -} - -type RecorderConfig struct { - Url string `yaml:"Url"` - Token string `yaml:"Token"` - Timeout int `yaml:"Timeout"` -} - -type RedisConfig struct { - Address string `yaml:"Address"` - Password string `yaml:"Password"` - Db int `yaml:"Db"` - Expiry int `json:"Expiry"` -} - -type IpReportConfig struct { - Periodic int `yaml:"Periodic"` - Type string `yaml:"Type"` - RecorderConfig *RecorderConfig `yaml:"RecorderConfig"` - RedisConfig *RedisConfig `yaml:"RedisConfig"` - EnableIpSync bool `yaml:"EnableIpSync"` -} - -type DynamicSpeedLimitConfig struct { - Periodic int `yaml:"Periodic"` - Traffic int64 `yaml:"Traffic"` - SpeedLimit int `yaml:"SpeedLimit"` - ExpireTime int `yaml:"ExpireTime"` -} - -type CertConfig struct { - CertMode string `yaml:"CertMode"` // none, file, http, dns - RejectUnknownSni bool `yaml:"RejectUnknownSni"` - CertDomain string `yaml:"CertDomain"` - CertFile string `yaml:"CertFile"` - KeyFile string `yaml:"KeyFile"` - Provider string `yaml:"Provider"` // alidns, cloudflare, gandi, godaddy.... - Email string `yaml:"Email"` - DNSEnv map[string]string `yaml:"DNSEnv"` - RealityConfig *RealityConfig `yaml:"RealityConfig"` -} diff --git a/conf/old.go b/conf/old.go deleted file mode 100644 index f8be2e2..0000000 --- a/conf/old.go +++ /dev/null @@ -1,81 +0,0 @@ -package conf - -import "log" - -type OldConfig struct { - NodesConfig []*struct { - ApiConfig *OldApiConfig `yaml:"ApiConfig"` - ControllerConfig *OldControllerConfig `yaml:"ControllerConfig"` - } `yaml:"Nodes"` -} - -type OldControllerConfig struct { - ListenIP string `yaml:"ListenIP"` - SendIP string `yaml:"SendIP"` - EnableDNS bool `yaml:"EnableDNS"` - DNSType string `yaml:"DNSType"` - DisableUploadTraffic bool `yaml:"DisableUploadTraffic"` - DisableGetRule bool `yaml:"DisableGetRule"` - EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` - EnableFallback bool `yaml:"EnableFallback"` - DisableIVCheck bool `yaml:"DisableIVCheck"` - DisableSniffing bool `yaml:"DisableSniffing"` - FallBackConfigs []*FallBackConfig `yaml:"FallBackConfigs"` - EnableIpRecorder bool `yaml:"EnableIpRecorder"` - IpRecorderConfig *IpReportConfig `yaml:"IpRecorderConfig"` - EnableDynamicSpeedLimit bool `yaml:"EnableDynamicSpeedLimit"` - DynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:"DynamicSpeedLimitConfig"` - CertConfig *CertConfig `yaml:"CertConfig"` -} - -type OldApiConfig struct { - APIHost string `yaml:"ApiHost"` - NodeID int `yaml:"NodeID"` - Key string `yaml:"ApiKey"` - NodeType string `yaml:"NodeType"` - EnableVless bool `yaml:"EnableVless"` - Timeout int `yaml:"Timeout"` - SpeedLimit int `yaml:"SpeedLimit"` - DeviceLimit int `yaml:"DeviceLimit"` - RuleListPath string `yaml:"RuleListPath"` - DisableCustomConfig bool `yaml:"DisableCustomConfig"` -} - -func migrateOldConfig(c *Conf, old *OldConfig) { - changed := false - for i, n := range c.NodesConfig { - if i >= len(old.NodesConfig) { - break - } - // limit config - if old.NodesConfig[i].ApiConfig.SpeedLimit != 0 { - n.ControllerConfig.LimitConfig.SpeedLimit = old.NodesConfig[i].ApiConfig.SpeedLimit - changed = true - } - if old.NodesConfig[i].ApiConfig.DeviceLimit != 0 { - n.ControllerConfig.LimitConfig.IPLimit = old.NodesConfig[i].ApiConfig.DeviceLimit - changed = true - } - if old.NodesConfig[i].ControllerConfig.EnableDynamicSpeedLimit { - n.ControllerConfig.LimitConfig.EnableDynamicSpeedLimit = true - changed = true - } - if old.NodesConfig[i].ControllerConfig.DynamicSpeedLimitConfig != nil { - n.ControllerConfig.LimitConfig.DynamicSpeedLimitConfig = - old.NodesConfig[i].ControllerConfig.DynamicSpeedLimitConfig - changed = true - } - if old.NodesConfig[i].ControllerConfig.EnableIpRecorder { - n.ControllerConfig.LimitConfig.EnableIpRecorder = true - changed = true - } - if old.NodesConfig[i].ControllerConfig.IpRecorderConfig != nil { - n.ControllerConfig.LimitConfig.IpRecorderConfig = - old.NodesConfig[i].ControllerConfig.IpRecorderConfig - changed = true - } - } - if changed { - log.Println("Warning: This config file is old.") - } -} diff --git a/conf/option.go b/conf/option.go new file mode 100644 index 0000000..1fc389b --- /dev/null +++ b/conf/option.go @@ -0,0 +1,36 @@ +package conf + +type Options struct { + ListenIP string `yaml:"ListenIP"` + SendIP string `yaml:"SendIP"` + LimitConfig LimitConfig `yaml:"LimitConfig"` + CertConfig *CertConfig `yaml:"CertConfig"` + XrayOptions XrayOptions `yaml:"-"` + HyOptions HyOptions `yaml:"-"` +} + +type XrayOptions struct { + EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` + EnableDNS bool `yaml:"EnableDNS"` + DNSType string `yaml:"DNSType"` + EnableUot bool `yaml:"EnableUot"` + EnableTFO bool `yaml:"EnableTFO"` + DisableIVCheck bool `yaml:"DisableIVCheck"` + DisableSniffing bool `yaml:"DisableSniffing"` + EnableFallback bool `yaml:"EnableFallback"` + FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"` +} + +type FallBackConfig struct { + SNI string `yaml:"SNI"` + Alpn string `yaml:"Alpn"` + Path string `yaml:"Path"` + Dest string `yaml:"Dest"` + ProxyProtocolVer uint64 `yaml:"ProxyProtocolVer"` +} + +type HyOptions struct { + Resolver string `yaml:"Resolver"` + ResolvePreference string `yaml:"ResolvePreference"` + SendDevice string `yaml:"SendDevice"` +} diff --git a/conf/sing.go b/conf/sing.go new file mode 100644 index 0000000..d734642 --- /dev/null +++ b/conf/sing.go @@ -0,0 +1,22 @@ +package conf + +type SingConfig struct { + LogConfig SingLogConfig `yaml:"LogConfig"` + OriginalPath string `yaml:"OriginalPath"` +} + +type SingLogConfig struct { + Disabled bool `yaml:"Disable"` + Level string `yaml:"Level"` + Output string `yaml:"Output"` + Timestamp bool `yaml:"Timestamp"` +} + +func NewSingConfig() *SingConfig { + return &SingConfig{ + LogConfig: SingLogConfig{ + Level: "error", + Timestamp: true, + }, + } +} diff --git a/conf/xray.go b/conf/xray.go new file mode 100644 index 0000000..d43c60d --- /dev/null +++ b/conf/xray.go @@ -0,0 +1,47 @@ +package conf + +type XrayConfig struct { + LogConfig *XrayLogConfig `yaml:"Log"` + AssetPath string `yaml:"AssetPath"` + DnsConfigPath string `yaml:"DnsConfigPath"` + RouteConfigPath string `yaml:"RouteConfigPath"` + ConnectionConfig *XrayConnectionConfig `yaml:"XrayConnectionConfig"` + InboundConfigPath string `yaml:"InboundConfigPath"` + OutboundConfigPath string `yaml:"OutboundConfigPath"` +} + +type XrayLogConfig struct { + Level string `yaml:"Level"` + AccessPath string `yaml:"AccessPath"` + ErrorPath string `yaml:"ErrorPath"` +} + +type XrayConnectionConfig struct { + Handshake uint32 `yaml:"handshake"` + ConnIdle uint32 `yaml:"connIdle"` + UplinkOnly uint32 `yaml:"uplinkOnly"` + DownlinkOnly uint32 `yaml:"downlinkOnly"` + BufferSize int32 `yaml:"bufferSize"` +} + +func NewXrayConfig() *XrayConfig { + return &XrayConfig{ + LogConfig: &XrayLogConfig{ + Level: "warning", + AccessPath: "", + ErrorPath: "", + }, + AssetPath: "/etc/V2bX/", + DnsConfigPath: "", + InboundConfigPath: "", + OutboundConfigPath: "", + RouteConfigPath: "", + ConnectionConfig: &XrayConnectionConfig{ + Handshake: 4, + ConnIdle: 30, + UplinkOnly: 2, + DownlinkOnly: 4, + BufferSize: 64, + }, + } +} diff --git a/core/hy/node.go b/core/hy/node.go index 1893577..4a92020 100644 --- a/core/hy/node.go +++ b/core/hy/node.go @@ -9,7 +9,7 @@ import ( "github.com/Yuzuki616/V2bX/limiter" ) -func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) error { +func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.Options) error { if info.Type != "hysteria" { return errors.New("the core not support " + info.Type) } diff --git a/core/hy/server.go b/core/hy/server.go index 28e790e..ac4e4e8 100644 --- a/core/hy/server.go +++ b/core/hy/server.go @@ -49,7 +49,7 @@ func NewServer(tag string, l *limiter.Limiter) *Server { } } -func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error { +func (s *Server) runServer(node *panel.NodeInfo, c *conf.Options) error { /*if c.HyOptions == nil { return errors.New("hy options is not vail") }*/ diff --git a/core/hy/server_test.go b/core/hy/server_test.go index c690968..18fb1c1 100644 --- a/core/hy/server_test.go +++ b/core/hy/server_test.go @@ -22,7 +22,7 @@ func TestServer(t *testing.T) { UpMbps: 100, DownMbps: 100, HyObfs: "atresssdaaaadd", - }, &conf.ControllerConfig{ + }, &conf.Options{ ListenIP: "127.0.0.1", HyOptions: conf.HyOptions{}, CertConfig: &conf.CertConfig{ @@ -36,7 +36,7 @@ func TestServer(t *testing.T) { time.Sleep(10 * time.Second) auth := base64.StdEncoding.EncodeToString([]byte("test1111")) log.Println(auth) - log.Println(s.counter.getCounters(auth).UpCounter.Load()) + log.Println(s.counter.GetUpCount(auth)) } }() select {} diff --git a/core/interface.go b/core/interface.go index af29058..5f60c46 100644 --- a/core/interface.go +++ b/core/interface.go @@ -7,14 +7,14 @@ import ( type AddUsersParams struct { Tag string - Config *conf.ControllerConfig + Config *conf.Options UserInfo []panel.UserInfo NodeInfo *panel.NodeInfo } type Core interface { Start() error Close() error - AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error + AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error DelNode(tag string) error AddUsers(p *AddUsersParams) (added int, err error) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) diff --git a/core/selector.go b/core/selector.go index 4c415e8..dd224db 100644 --- a/core/selector.go +++ b/core/selector.go @@ -39,7 +39,7 @@ func isSupported(protocol string, protocols []string) bool { return false } -func (s *Selector) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error { +func (s *Selector) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error { for i := range s.cores { if !isSupported(info.Type, s.cores[i].Protocols()) { continue diff --git a/core/sing/hook.go b/core/sing/hook.go index 6b02dd5..974bde5 100644 --- a/core/sing/hook.go +++ b/core/sing/hook.go @@ -2,6 +2,12 @@ package sing import ( "net" + "strings" + "sync" + + "github.com/Yuzuki616/V2bX/common/rate" + + "github.com/Yuzuki616/V2bX/limiter" "github.com/Yuzuki616/V2bX/common/counter" "github.com/inazumav/sing-box/adapter" @@ -17,7 +23,7 @@ func NewHookServer(logger log.Logger) *HookServer { return &HookServer{ hooker: &Hooker{ logger: logger, - counter: make(map[string]*counter.TrafficCounter), + counter: sync.Map{}, }, } } @@ -40,19 +46,37 @@ func (h *HookServer) Hooker() *Hooker { type Hooker struct { logger log.Logger - counter map[string]*counter.TrafficCounter + counter sync.Map } func (h *Hooker) RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn { - if c, ok := h.counter[inbound]; ok { + l, err := limiter.GetLimiter(inbound) + if err != nil { + log.Error("get limiter for ", inbound, " error: ", err) + } + ip, _, _ := strings.Cut(conn.RemoteAddr().String(), ":") + if b, r := l.CheckLimit(user, ip, true); r { + conn.Close() + h.logger.Error("[", inbound, "] ", "Limited ", user, " by ip or conn") + return conn + } else if b != nil { + conn = rate.NewConnRateLimiter(conn, b) + } + if c, ok := h.counter.Load(inbound); ok { + return counter.NewConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(user)) + } else { + c := counter.NewTrafficCounter() + h.counter.Store(inbound, c) return counter.NewConnCounter(conn, c.GetCounter(user)) } - return conn } func (h *Hooker) RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn { - if c, ok := h.counter[inbound]; ok { + if c, ok := h.counter.Load(inbound); ok { + return counter.NewPacketConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(user)) + } else { + c := counter.NewTrafficCounter() + h.counter.Store(inbound, c) return counter.NewPacketConnCounter(conn, c.GetCounter(user)) } - return conn } diff --git a/core/sing/node.go b/core/sing/node.go index e938919..76a5f27 100644 --- a/core/sing/node.go +++ b/core/sing/node.go @@ -21,12 +21,17 @@ import ( ) type WsNetworkConfig struct { - Path string `json:"path"` + Path string `json:"path"` + Headers map[string]string `json:"headers"` } -func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) (option.Inbound, error) { - addr, _ := netip.ParseAddr("0.0.0.0") +func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (option.Inbound, error) { + addr, err := netip.ParseAddr(c.ListenIP) + if err != nil { + return option.Inbound{}, fmt.Errorf("the listen ip not vail") + } listen := option.ListenOptions{ + //ProxyProtocol: true, Listen: (*option.ListenAddress)(&addr), ListenPort: uint16(info.Port), } @@ -47,6 +52,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.ControllerConfi } switch info.Network { case "tcp": + t.Type = "" case "ws": network := WsNetworkConfig{} err := json.Unmarshal(info.NetworkSettings, &network) @@ -59,14 +65,22 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.ControllerConfi return option.Inbound{}, fmt.Errorf("parse path error: %s", err) } ed, _ := strconv.Atoi(u.Query().Get("ed")) + h := make(map[string]option.Listable[string], len(network.Headers)) + for k, v := range network.Headers { + h[k] = option.Listable[string]{ + v, + } + } t.WebsocketOptions = option.V2RayWebsocketOptions{ Path: u.Path, EarlyDataHeaderName: "Sec-WebSocket-Protocol", MaxEarlyData: uint32(ed), + Headers: h, } case "grpc": - t.GRPCOptions = option.V2RayGRPCOptions{ - ServiceName: info.ServerName, + err := json.Unmarshal(info.NetworkSettings, &t.GRPCOptions) + if err != nil { + return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err) } } in.VMessOptions = option.VMessInboundOptions{ @@ -96,7 +110,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.ControllerConfi return in, nil } -func (b *Box) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error { +func (b *Box) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error { c, err := getInboundOptions(tag, info, config) if err != nil { return err diff --git a/core/sing/sing.go b/core/sing/sing.go index 2f28ea9..245a1b4 100644 --- a/core/sing/sing.go +++ b/core/sing/sing.go @@ -39,8 +39,14 @@ func init() { vCore.RegisterCore("sing", New) } -func New(_ *conf.CoreConfig) (vCore.Core, error) { +func New(c *conf.CoreConfig) (vCore.Core, error) { options := option.Options{} + options.Log = &option.LogOptions{ + Disabled: c.SingConfig.LogConfig.Disabled, + Level: c.SingConfig.LogConfig.Level, + Timestamp: c.SingConfig.LogConfig.Timestamp, + Output: c.SingConfig.LogConfig.Output, + } ctx := context.Background() createdAt := time.Now() experimentalOptions := common.PtrValueOrDefault(options.Experimental) diff --git a/core/sing/user.go b/core/sing/user.go index 602c6a5..ac7af2b 100644 --- a/core/sing/user.go +++ b/core/sing/user.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/Yuzuki616/V2bX/api/panel" + "github.com/Yuzuki616/V2bX/common/counter" "github.com/Yuzuki616/V2bX/core" "github.com/inazumav/sing-box/inbound" "github.com/inazumav/sing-box/option" @@ -36,7 +37,8 @@ func (b *Box) AddUsers(p *core.AddUsersParams) (added int, err error) { } func (b *Box) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) { - if c, ok := b.hookServer.Hooker().counter[tag]; ok { + if v, ok := b.hookServer.Hooker().counter.Load(tag); ok { + c := v.(*counter.TrafficCounter) up = c.GetUpCount(uuid) down = c.GetDownCount(uuid) if reset { diff --git a/core/xray/inbound.go b/core/xray/inbound.go index 1060574..c9b9f4a 100644 --- a/core/xray/inbound.go +++ b/core/xray/inbound.go @@ -17,7 +17,7 @@ import ( ) // BuildInbound build Inbound config for different protocol -func buildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.InboundHandlerConfig, error) { +func buildInbound(config *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*core.InboundHandlerConfig, error) { in := &coreConf.InboundDetourConfig{} // Set network protocol t := coreConf.TransportProtocol(nodeInfo.Network) @@ -150,7 +150,7 @@ func buildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s return in.Build() } -func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { +func buildV2ray(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { if nodeInfo.ExtraConfig.EnableVless == "true" { //Set vless inbound.Protocol = "vless" @@ -213,7 +213,7 @@ func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound return nil } -func buildTrojan(config *conf.ControllerConfig, inbound *coreConf.InboundDetourConfig) error { +func buildTrojan(config *conf.Options, inbound *coreConf.InboundDetourConfig) error { inbound.Protocol = "trojan" if config.XrayOptions.EnableFallback { // Set fallback @@ -237,7 +237,7 @@ func buildTrojan(config *conf.ControllerConfig, inbound *coreConf.InboundDetourC return nil } -func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { +func buildShadowsocks(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { inbound.Protocol = "shadowsocks" settings := &coreConf.ShadowsocksServerConfig{ Cipher: nodeInfo.Cipher, diff --git a/core/xray/node.go b/core/xray/node.go index 062b508..70a52a0 100644 --- a/core/xray/node.go +++ b/core/xray/node.go @@ -11,7 +11,7 @@ import ( "github.com/xtls/xray-core/features/outbound" ) -func (c *Core) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error { +func (c *Core) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error { inboundConfig, err := buildInbound(config, info, tag) if err != nil { return fmt.Errorf("build inbound error: %s", err) diff --git a/core/xray/outbound.go b/core/xray/outbound.go index cb3a140..b786505 100644 --- a/core/xray/outbound.go +++ b/core/xray/outbound.go @@ -11,7 +11,7 @@ import ( ) // BuildOutbound build freedom outbund config for addoutbound -func buildOutbound(config *conf2.ControllerConfig, tag string) (*core.OutboundHandlerConfig, error) { +func buildOutbound(config *conf2.Options, tag string) (*core.OutboundHandlerConfig, error) { outboundDetourConfig := &conf.OutboundDetourConfig{} outboundDetourConfig.Protocol = "freedom" outboundDetourConfig.Tag = tag diff --git a/core/xray/xray.go b/core/xray/xray.go index b11baa2..cd56604 100644 --- a/core/xray/xray.go +++ b/core/xray/xray.go @@ -39,7 +39,7 @@ func New(c *conf.CoreConfig) (vCore.Core, error) { return &Core{Server: getCore(c.XrayConfig)}, nil } -func parseConnectionConfig(c *conf.ConnectionConfig) (policy *coreConf.Policy) { +func parseConnectionConfig(c *conf.XrayConnectionConfig) (policy *coreConf.Policy) { policy = &coreConf.Policy{ StatsUserUplink: true, StatsUserDownlink: true, diff --git a/go.mod b/go.mod index 50a48cf..b55f6b0 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/go-resty/resty/v2 v2.7.0 github.com/goccy/go-json v0.10.2 github.com/hashicorp/go-multierror v1.1.1 - github.com/inazumav/sing-box v0.0.0-20230728005925-1df6eb0f5bf7 + github.com/inazumav/sing-box v0.0.0-20230728123002-eb2ba58e499a github.com/juju/ratelimit v1.0.2 github.com/oschwald/geoip2-golang v1.9.0 github.com/sagernet/sing v0.2.9 diff --git a/go.sum b/go.sum index 13f9cc4..7e3407b 100644 --- a/go.sum +++ b/go.sum @@ -379,10 +379,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= -github.com/inazumav/sing-box v0.0.0-20230728005628-9c73947a55ad h1:Ho7F+U1n41dX6c+3TjojaCj1NjBlaaCa1Vjtll9BVW4= -github.com/inazumav/sing-box v0.0.0-20230728005628-9c73947a55ad/go.mod h1:W91us/coe3lvl5jCtw2n6acyagpRbOO16h1IV3/0nrc= -github.com/inazumav/sing-box v0.0.0-20230728005925-1df6eb0f5bf7 h1:fHFY6wVkFNFmL+LXVnU+zvrEoULqbZ1eVYCoKsb6/dQ= -github.com/inazumav/sing-box v0.0.0-20230728005925-1df6eb0f5bf7/go.mod h1:W91us/coe3lvl5jCtw2n6acyagpRbOO16h1IV3/0nrc= +github.com/inazumav/sing-box v0.0.0-20230728123002-eb2ba58e499a h1:/FGmhOCu6LYt8zd6DpMEvuHlPnUPMr0zziyJkuU1wVI= +github.com/inazumav/sing-box v0.0.0-20230728123002-eb2ba58e499a/go.mod h1:W91us/coe3lvl5jCtw2n6acyagpRbOO16h1IV3/0nrc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= diff --git a/node/controller.go b/node/controller.go index 4f389cd..eba90b6 100644 --- a/node/controller.go +++ b/node/controller.go @@ -26,15 +26,15 @@ type Controller struct { renewCertPeriodic *task.Task dynamicSpeedLimitPeriodic *task.Task onlineIpReportPeriodic *task.Task - *conf.ControllerConfig + *conf.Options } // NewController return a Node controller with default parameters. -func NewController(server vCore.Core, api *panel.Client, config *conf.ControllerConfig) *Controller { +func NewController(server vCore.Core, api *panel.Client, config *conf.Options) *Controller { controller := &Controller{ - server: server, - ControllerConfig: config, - apiClient: api, + server: server, + Options: config, + apiClient: api, } return controller } @@ -71,13 +71,13 @@ func (c *Controller) Start() error { } } // Add new tag - err = c.server.AddNode(c.tag, node, c.ControllerConfig) + err = c.server.AddNode(c.tag, node, c.Options) if err != nil { return fmt.Errorf("add new node error: %s", err) } added, err := c.server.AddUsers(&vCore.AddUsersParams{ Tag: c.tag, - Config: c.ControllerConfig, + Config: c.Options, UserInfo: c.userList, NodeInfo: node, }) diff --git a/node/node.go b/node/node.go index ae56252..2f8b847 100644 --- a/node/node.go +++ b/node/node.go @@ -2,6 +2,7 @@ package node import ( "fmt" + "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" vCore "github.com/Yuzuki616/V2bX/core" @@ -23,7 +24,7 @@ func (n *Node) Start(nodes []*conf.NodeConfig, core vCore.Core) error { return err } // Register controller service - n.controllers[i] = NewController(core, p, c.ControllerConfig) + n.controllers[i] = NewController(core, p, c.Options) err = n.controllers[i].Start() if err != nil { return fmt.Errorf("start node controller [%s-%s-%d] error: %s", diff --git a/node/task.go b/node/task.go index 817d1af..c1fcf24 100644 --- a/node/task.go +++ b/node/task.go @@ -100,7 +100,7 @@ func (c *Controller) nodeInfoMonitor() (err error) { } } // add new node - err = c.server.AddNode(c.tag, newNodeInfo, c.ControllerConfig) + err = c.server.AddNode(c.tag, newNodeInfo, c.Options) if err != nil { log.WithFields(log.Fields{ "tag": c.tag, @@ -110,7 +110,7 @@ func (c *Controller) nodeInfoMonitor() (err error) { } _, err = c.server.AddUsers(&vCore.AddUsersParams{ Tag: c.tag, - Config: c.ControllerConfig, + Config: c.Options, UserInfo: c.userList, NodeInfo: newNodeInfo, }) @@ -168,7 +168,7 @@ func (c *Controller) nodeInfoMonitor() (err error) { // have added users _, err = c.server.AddUsers(&vCore.AddUsersParams{ Tag: c.tag, - Config: c.ControllerConfig, + Config: c.Options, UserInfo: added, }) if err != nil {