Compare commits

...

28 Commits

Author SHA1 Message Date
wyx2685
7184e49650 尝试修复其他面板兼容问题 2024-09-03 01:03:37 +09:00
wyx2685
ea0b7d8f40 面板无设备数列表接口时暂不报错 2024-09-01 05:27:11 +09:00
wyx2685
12fbcb1460 修复MAP未初始化 2024-08-31 17:19:13 +09:00
wyx2685
c6d48e1edf 升级内核 2024-08-31 17:06:00 +09:00
wyx2685
8d7168c6a4 尝试修复在线设备数上报后有概率被限制链接 2024-08-31 16:02:50 +09:00
wyx2685
173c48a76f 调整设备数限制实现方式 2024-08-30 06:48:41 +09:00
wyx2685
130e94cf45 Merge branch 'InazumaV:dev_new' into dev_new 2024-08-29 09:49:15 +08:00
wyx2685
89ddfff060 升级sing-box内核 2024-08-14 01:48:33 +09:00
wyx2685
07d49293d8 BUG修复前不对UDP链接进行限速 2024-08-14 01:35:30 +09:00
wyx2685
9e8f87740e update xray-core 1.8.23 2024-07-30 01:28:39 +09:00
wyx2685
29a99985c8 Fix:优化流量统计并发情况可能存在的冲突 2024-07-28 13:54:47 +09:00
wyx2685
248ff3764f update sing-box core 2024-07-24 19:50:55 +09:00
wyx2685
3dfeba7e68 xray-core 1.8.21 2024-07-22 07:49:04 +09:00
wyx2685
8eb623b3f0 update xray core 2024-07-21 20:56:35 +09:00
wyx2685
cdcbddd464 update core 2024-07-21 06:14:16 +09:00
wyx2685
e81d47321b xray-core 1.8.19 2024-07-17 23:26:09 +09:00
wyx2685
4d82eff518 update Xray-core 1.8.18 2024-07-17 09:21:57 +09:00
wyx2685
b96545649b Fix:修复无法使用Sing-box内核urltest出口的问题 2024-07-15 00:33:54 +09:00
wyx2685
8757b955a6 修复hy2内核设备数统计错误的BUG 2024-07-08 20:32:52 +09:00
wyx2685
33d9ab4b0a 适应xray1.8.16更改 2024-07-08 18:12:09 +09:00
wyx2685
a7637d436f 更新所有内核 2024-07-08 17:44:22 +09:00
wyx2685
4dda65a636 Fix:修复Xray内核VMess/VLess的Websocket传输方式参数设置错误 2024-06-24 18:55:04 +09:00
wyx2685
6c725b424f 更新文档链接 2024-06-24 04:48:15 +09:00
wyx2685
a052a1f1e8 升级Xray内核,完善singbox内核自定义配置 2024-06-24 04:41:56 +09:00
wyx2685
85ad40d098 升级全部内核 2024-06-13 00:06:22 +09:00
riolu.rs
a85352c402 fix(xray.ss) ss2022 users 2024-05-02 22:53:09 +08:00
wyx2685
206af0216c 修复xray内核vless协议bug,升级xray/sing内核 2024-03-13 22:15:04 +09:00
wyx2685
ed5edda28a 修复Sing内核trojan用户列表BUG、修复用户设备限制不生效 2024-03-04 22:25:06 +09:00
32 changed files with 952 additions and 729 deletions

View File

@@ -1,16 +1,8 @@
name: Publish Docker image name: Publish Docker image
on: on:
workflow_dispatch: workflow_dispatch:
push: release:
branches: types: [published]
- dev_new
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
tags:
- 'v*'
pull_request: pull_request:
branches: branches:
- 'dev_new' - 'dev_new'

View File

@@ -52,17 +52,17 @@ wget -N https://raw.githubusercontent.com/wyx2685/V2bX-script/master/install.sh
### 手动安装 ### 手动安装
[手动安装教程(过时待更新)](https://yuzuki-1.gitbook.io/v2bx-doc/xrayr-xia-zai-he-an-zhuang/install/manual) [手动安装教程](https://v2bx.v-50.me/v2bx/v2bx-xia-zai-he-an-zhuang/install/manual)
## 构建 ## 构建
``` bash ``` bash
# 通过-tags选项指定要编译的内核 可选 xray sing # 通过-tags选项指定要编译的内核 可选 xray sing
go build -o V2bX -ldflags '-s -w' -gcflags="all=-trimpath=${PWD}" -asmflags="all=-trimpath=${PWD} -tags "xray sing" go build -v -o ./V2bX -tags "sing hysteria2 with_reality_server with_quic with_grpc with_utls with_wireguard with_acme" -trimpath -ldflags "-s -w -buildid="
``` ```
## 配置文件及详细使用教程 ## 配置文件及详细使用教程
[详细使用教程](https://yuzuki-1.gitbook.io/v2bx-doc/) [详细使用教程](https://v2bx.v-50.me/)
## 免责声明 ## 免责声明
@@ -82,4 +82,4 @@ go build -o V2bX -ldflags '-s -w' -gcflags="all=-trimpath=${PWD}" -asmflags="all
## Stars 增长记录 ## Stars 增长记录
[![Stargazers over time](https://starchart.cc/InazumaV/V2bX.svg)](https://starchart.cc/InazumaV/V2bX) [![Stargazers over time](https://starchart.cc/wyx2685/V2bX.svg)](https://starchart.cc/wyx2685/V2bX)

View File

@@ -2,7 +2,6 @@ package panel
import ( import (
"crypto/sha256" "crypto/sha256"
"encoding/base64"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"reflect" "reflect"
@@ -10,7 +9,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/InazumaV/V2bX/common/crypt"
"github.com/goccy/go-json" "github.com/goccy/go-json"
) )
@@ -80,7 +78,7 @@ type TlsSettings struct {
ServerPort string `json:"server_port"` ServerPort string `json:"server_port"`
ShortId string `json:"short_id"` ShortId string `json:"short_id"`
PrivateKey string `json:"private_key"` PrivateKey string `json:"private_key"`
Xver uint8 `json:"xver,string"` Xver uint64 `json:"xver,string"`
} }
type RealityConfig struct { type RealityConfig struct {
@@ -186,18 +184,6 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
cm = &rsp.CommonNode cm = &rsp.CommonNode
node.VAllss = rsp node.VAllss = rsp
node.Security = node.VAllss.Tls node.Security = node.VAllss.Tls
if len(rsp.NetworkSettings) > 0 {
err = json.Unmarshal(rsp.NetworkSettings, &rsp.RealityConfig)
if err != nil {
return nil, fmt.Errorf("decode reality config error: %s", err)
}
}
if node.Security == Reality {
if rsp.TlsSettings.PrivateKey == "" {
key := crypt.GenX25519Private([]byte("vless" + c.Token))
rsp.TlsSettings.PrivateKey = base64.RawURLEncoding.EncodeToString(key)
}
}
case "shadowsocks": case "shadowsocks":
rsp := &ShadowsocksNode{} rsp := &ShadowsocksNode{}
err = json.Unmarshal(r.Body(), rsp) err = json.Unmarshal(r.Body(), rsp)

View File

@@ -24,7 +24,8 @@ type Client struct {
nodeEtag string nodeEtag string
userEtag string userEtag string
responseBodyHash string responseBodyHash string
LastReportOnline map[int]int UserList *UserListBody
AliveMap *AliveMap
} }
func New(c *conf.ApiConfig) (*Client, error) { func New(c *conf.ApiConfig) (*Client, error) {
@@ -71,5 +72,7 @@ func New(c *conf.ApiConfig) (*Client, error) {
APIHost: c.APIHost, APIHost: c.APIHost,
NodeType: c.NodeType, NodeType: c.NodeType,
NodeId: c.NodeID, NodeId: c.NodeID,
UserList: &UserListBody{},
AliveMap: &AliveMap{},
}, nil }, nil
} }

View File

@@ -16,7 +16,6 @@ type UserInfo struct {
Uuid string `json:"uuid"` Uuid string `json:"uuid"`
SpeedLimit int `json:"speed_limit"` SpeedLimit int `json:"speed_limit"`
DeviceLimit int `json:"device_limit"` DeviceLimit int `json:"device_limit"`
AliveIp int `json:"alive_ip"`
} }
type UserListBody struct { type UserListBody struct {
@@ -24,8 +23,12 @@ type UserListBody struct {
Users []UserInfo `json:"users"` Users []UserInfo `json:"users"`
} }
// GetUserList will pull user form sspanel type AliveMap struct {
func (c *Client) GetUserList() (UserList []UserInfo, err error) { Alive map[int]int `json:"alive"`
}
// GetUserList will pull user from v2board
func (c *Client) GetUserList() ([]UserInfo, error) {
const path = "/api/v1/server/UniProxy/user" const path = "/api/v1/server/UniProxy/user"
r, err := c.client.R(). r, err := c.client.R().
SetHeader("If-None-Match", c.userEtag). SetHeader("If-None-Match", c.userEtag).
@@ -34,51 +37,44 @@ func (c *Client) GetUserList() (UserList []UserInfo, err error) {
if err = c.checkResponse(r, path, err); err != nil { if err = c.checkResponse(r, path, err); err != nil {
return nil, err return nil, err
} }
if r != nil { if r != nil {
defer func() { defer r.RawResponse.Body.Close()
if r.RawBody() != nil {
r.RawBody().Close()
}
}()
if r.StatusCode() == 304 {
return nil, nil
}
} else { } else {
return nil, fmt.Errorf("received nil response") return nil, fmt.Errorf("received nil response")
} }
var userList *UserListBody
if err != nil {
return nil, fmt.Errorf("read body error: %s", err)
}
if err := json.Unmarshal(r.Body(), &userList); err != nil {
return nil, fmt.Errorf("unmarshal userlist error: %s", err)
}
c.userEtag = r.Header().Get("ETag")
var userinfos []UserInfo if r.StatusCode() == 304 {
var localDeviceLimit int = 0 return nil, nil
for _, user := range userList.Users { } else {
// If there is still device available, add the user if err := json.Unmarshal(r.Body(), c.UserList); err != nil {
if user.DeviceLimit > 0 && user.AliveIp > 0 { return nil, fmt.Errorf("unmarshal user list error: %w", err)
lastOnline := 0
if v, ok := c.LastReportOnline[user.Id]; ok {
lastOnline = v
}
// If there are any available device.
localDeviceLimit = user.DeviceLimit - user.AliveIp + lastOnline
if localDeviceLimit > 0 {
} else if lastOnline > 0 {
} else {
continue
}
} }
userinfos = append(userinfos, user) c.userEtag = r.Header().Get("ETag")
}
return c.UserList.Users, nil
}
// GetUserAlive will fetch the alive_ip count for users
func (c *Client) GetUserAlive() (map[int]int, error) {
const path = "/api/v1/server/UniProxy/alivelist"
r, err := c.client.R().
ForceContentType("application/json").
Get(path)
if err != nil || r.StatusCode() >= 399 {
return make(map[int]int), nil
} }
return userinfos, nil if r != nil {
defer r.RawResponse.Body.Close()
} else {
return nil, fmt.Errorf("received nil response")
}
if err := json.Unmarshal(r.Body(), c.AliveMap); err != nil {
return nil, fmt.Errorf("unmarshal user alive list error: %s", err)
}
return c.AliveMap.Alive, nil
} }
type UserTraffic struct { type UserTraffic struct {
@@ -105,8 +101,7 @@ func (c *Client) ReportUserTraffic(userTraffic []UserTraffic) error {
return nil return nil
} }
func (c *Client) ReportNodeOnlineUsers(data *map[int][]string, reportOnline *map[int]int) error { func (c *Client) ReportNodeOnlineUsers(data *map[int][]string) error {
c.LastReportOnline = *reportOnline
const path = "/api/v1/server/UniProxy/alive" const path = "/api/v1/server/UniProxy/alive"
r, err := c.client.R(). r, err := c.client.R().
SetBody(data). SetBody(data).

View File

@@ -6,8 +6,7 @@ import (
) )
type TrafficCounter struct { type TrafficCounter struct {
counters map[string]*TrafficStorage counters sync.Map
lock sync.RWMutex
} }
type TrafficStorage struct { type TrafficStorage struct {
@@ -16,60 +15,52 @@ type TrafficStorage struct {
} }
func NewTrafficCounter() *TrafficCounter { func NewTrafficCounter() *TrafficCounter {
return &TrafficCounter{ return &TrafficCounter{}
counters: map[string]*TrafficStorage{},
}
} }
func (c *TrafficCounter) GetCounter(id string) *TrafficStorage { func (c *TrafficCounter) GetCounter(id string) *TrafficStorage {
c.lock.RLock() if cts, ok := c.counters.Load(id); ok {
cts, ok := c.counters[id] return cts.(*TrafficStorage)
c.lock.RUnlock()
if !ok {
cts = &TrafficStorage{}
c.counters[id] = cts
} }
return cts newStorage := &TrafficStorage{}
if cts, loaded := c.counters.LoadOrStore(id, newStorage); loaded {
return cts.(*TrafficStorage)
}
return newStorage
} }
func (c *TrafficCounter) GetUpCount(id string) int64 { func (c *TrafficCounter) GetUpCount(id string) int64 {
c.lock.RLock() if cts, ok := c.counters.Load(id); ok {
cts, ok := c.counters[id] return cts.(*TrafficStorage).UpCounter.Load()
c.lock.RUnlock()
if ok {
return cts.UpCounter.Load()
} }
return 0 return 0
} }
func (c *TrafficCounter) GetDownCount(id string) int64 { func (c *TrafficCounter) GetDownCount(id string) int64 {
c.lock.RLock() if cts, ok := c.counters.Load(id); ok {
cts, ok := c.counters[id] return cts.(*TrafficStorage).DownCounter.Load()
c.lock.RUnlock()
if ok {
return cts.DownCounter.Load()
} }
return 0 return 0
} }
func (c *TrafficCounter) Len() int { func (c *TrafficCounter) Len() int {
c.lock.RLock() length := 0
defer c.lock.RUnlock() c.counters.Range(func(_, _ interface{}) bool {
return len(c.counters) length++
return true
})
return length
} }
func (c *TrafficCounter) Reset(id string) { func (c *TrafficCounter) Reset(id string) {
c.lock.RLock() if cts, ok := c.counters.Load(id); ok {
cts := c.GetCounter(id) cts.(*TrafficStorage).UpCounter.Store(0)
c.lock.RUnlock() cts.(*TrafficStorage).DownCounter.Store(0)
cts.UpCounter.Store(0) }
cts.DownCounter.Store(0)
} }
func (c *TrafficCounter) Delete(id string) { func (c *TrafficCounter) Delete(id string) {
c.lock.Lock() c.counters.Delete(id)
delete(c.counters, id)
c.lock.Unlock()
} }
func (c *TrafficCounter) Rx(id string, n int) { func (c *TrafficCounter) Rx(id string, n int) {
@@ -81,11 +72,3 @@ func (c *TrafficCounter) Tx(id string, n int) {
cts := c.GetCounter(id) cts := c.GetCounter(id)
cts.UpCounter.Add(int64(n)) cts.UpCounter.Add(int64(n))
} }
func (c *TrafficCounter) IncConn(auth string) {
return
}
func (c *TrafficCounter) DecConn(auth string) {
return
}

View File

@@ -4,9 +4,6 @@ import (
"net" "net"
"github.com/juju/ratelimit" "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 { func NewConnRateLimiter(c net.Conn, l *ratelimit.Bucket) *Conn {
@@ -31,6 +28,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
return c.Conn.Write(b) return c.Conn.Write(b)
} }
/*
type PacketConnCounter struct { type PacketConnCounter struct {
network.PacketConn network.PacketConn
limiter *ratelimit.Bucket limiter *ratelimit.Bucket
@@ -47,10 +45,11 @@ func (p *PacketConnCounter) ReadPacket(buff *buf.Buffer) (destination M.Socksadd
pLen := buff.Len() pLen := buff.Len()
destination, err = p.PacketConn.ReadPacket(buff) destination, err = p.PacketConn.ReadPacket(buff)
p.limiter.Wait(int64(buff.Len() - pLen)) p.limiter.Wait(int64(buff.Len() - pLen))
return return destination, err
} }
func (p *PacketConnCounter) WritePacket(buff *buf.Buffer, destination M.Socksaddr) (err error) { func (p *PacketConnCounter) WritePacket(buff *buf.Buffer, destination M.Socksaddr) (err error) {
p.limiter.Wait(int64(buff.Len())) p.limiter.Wait(int64(buff.Len()))
return p.PacketConn.WritePacket(buff, destination) return p.PacketConn.WritePacket(buff, destination)
} }
*/

View File

@@ -8,7 +8,6 @@ type SingConfig struct {
LogConfig SingLogConfig `json:"Log"` LogConfig SingLogConfig `json:"Log"`
NtpConfig SingNtpConfig `json:"NTP"` NtpConfig SingNtpConfig `json:"NTP"`
EnableConnClear bool `json:"EnableConnClear"` EnableConnClear bool `json:"EnableConnClear"`
DnsConfigPath string `json:"DnsConfigPath"`
OriginalPath string `json:"OriginalPath"` OriginalPath string `json:"OriginalPath"`
} }

View File

@@ -13,11 +13,11 @@ import (
"github.com/InazumaV/V2bX/api/panel" "github.com/InazumaV/V2bX/api/panel"
"github.com/InazumaV/V2bX/conf" "github.com/InazumaV/V2bX/conf"
"github.com/apernet/hysteria/core/server" "github.com/apernet/hysteria/core/v2/server"
"github.com/apernet/hysteria/extras/correctnet" "github.com/apernet/hysteria/extras/v2/correctnet"
"github.com/apernet/hysteria/extras/masq" "github.com/apernet/hysteria/extras/v2/masq"
"github.com/apernet/hysteria/extras/obfs" "github.com/apernet/hysteria/extras/v2/obfs"
"github.com/apernet/hysteria/extras/outbounds" "github.com/apernet/hysteria/extras/v2/outbounds"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -49,7 +49,7 @@ const (
defaultStreamReceiveWindow = 8388608 // 8MB defaultStreamReceiveWindow = 8388608 // 8MB
defaultConnReceiveWindow = defaultStreamReceiveWindow * 5 / 2 // 20MB defaultConnReceiveWindow = defaultStreamReceiveWindow * 5 / 2 // 20MB
defaultMaxIdleTimeout = 30 * time.Second defaultMaxIdleTimeout = 30 * time.Second
defaultMaxIncomingStreams = 1024 defaultMaxIncomingStreams = 4096
defaultUDPIdleTimeout = 60 * time.Second defaultUDPIdleTimeout = 60 * time.Second
) )
@@ -268,7 +268,7 @@ func (n *Hysteria2node) getOutboundConfig(c *serverConfig) (server.Outbound, err
return Outbound, nil return Outbound, nil
} }
func (n *Hysteria2node) getMasqHandler(tlsconfig *server.TLSConfig, conn net.PacketConn, info *panel.NodeInfo, c *serverConfig) (http.Handler, error) { func (n *Hysteria2node) getMasqHandler(tlsconfig *server.TLSConfig, conn net.PacketConn, c *serverConfig) (http.Handler, error) {
var handler http.Handler var handler http.Handler
switch strings.ToLower(c.Masquerade.Type) { switch strings.ToLower(c.Masquerade.Type) {
case "", "404": case "", "404":
@@ -284,15 +284,23 @@ func (n *Hysteria2node) getMasqHandler(tlsconfig *server.TLSConfig, conn net.Pac
} }
u, err := url.Parse(c.Masquerade.Proxy.URL) u, err := url.Parse(c.Masquerade.Proxy.URL)
if err != nil { if err != nil {
return nil, fmt.Errorf(fmt.Sprintf("masquerade.proxy.url %s", err)) return nil, fmt.Errorf("masquerade.proxy.url %s", err)
} }
handler = &httputil.ReverseProxy{ handler = &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) { Director: func(req *http.Request) {
r.SetURL(u) req.URL.Scheme = u.Scheme
// SetURL rewrites the Host header, req.URL.Host = u.Host
// but we don't want that if rewriteHost is false
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
xff := req.Header.Get("X-Forwarded-For")
if xff != "" {
clientIP = xff + ", " + clientIP
}
req.Header.Set("X-Forwarded-For", clientIP)
}
if !c.Masquerade.Proxy.RewriteHost { if !c.Masquerade.Proxy.RewriteHost {
r.Out.Host = r.In.Host req.Host = req.URL.Host
} }
}, },
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
@@ -347,7 +355,7 @@ func (n *Hysteria2node) getMasqHandler(tlsconfig *server.TLSConfig, conn net.Pac
return MasqHandler, nil return MasqHandler, nil
} }
func (n *Hysteria2node) getHyConfig(tag string, info *panel.NodeInfo, config *conf.Options, c *serverConfig) (*server.Config, error) { func (n *Hysteria2node) getHyConfig(info *panel.NodeInfo, config *conf.Options, c *serverConfig) (*server.Config, error) {
tls, err := n.getTLSConfig(config) tls, err := n.getTLSConfig(config)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -364,7 +372,7 @@ func (n *Hysteria2node) getHyConfig(tag string, info *panel.NodeInfo, config *co
if err != nil { if err != nil {
return nil, err return nil, err
} }
Masq, err := n.getMasqHandler(tls, conn, info, c) Masq, err := n.getMasqHandler(tls, conn, c)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -7,8 +7,8 @@ import (
"os" "os"
"time" "time"
"github.com/apernet/hysteria/extras/outbounds/acl" "github.com/apernet/hysteria/extras/v2/outbounds/acl"
"github.com/apernet/hysteria/extras/outbounds/acl/v2geo" "github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo"
"go.uber.org/zap" "go.uber.org/zap"
) )

View File

@@ -4,17 +4,36 @@ import (
"sync" "sync"
"github.com/InazumaV/V2bX/common/counter" "github.com/InazumaV/V2bX/common/counter"
"github.com/InazumaV/V2bX/common/format"
"github.com/InazumaV/V2bX/limiter"
"go.uber.org/zap"
) )
type HookServer struct { type HookServer struct {
Tag string Tag string
logger *zap.Logger
Counter sync.Map Counter sync.Map
} }
func (h *HookServer) Log(id string, tx, rx uint64) (ok bool) { func (h *HookServer) LogTraffic(id string, tx, rx uint64) (ok bool) {
var c interface{} var c interface{}
var exists bool var exists bool
limiterinfo, err := limiter.GetLimiter(h.Tag)
if err != nil {
h.logger.Error("Get limiter error", zap.String("tag", h.Tag), zap.Error(err))
return false
}
userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(h.Tag, id))
if ok {
userlimitInfo := userLimit.(*limiter.UserLimitInfo)
if userlimitInfo.OverLimit {
userlimitInfo.OverLimit = false
return false
}
}
if c, exists = h.Counter.Load(h.Tag); !exists { if c, exists = h.Counter.Load(h.Tag); !exists {
c = counter.NewTrafficCounter() c = counter.NewTrafficCounter()
h.Counter.Store(h.Tag, c) h.Counter.Store(h.Tag, c)
@@ -28,3 +47,6 @@ func (h *HookServer) Log(id string, tx, rx uint64) (ok bool) {
return false return false
} }
func (s *HookServer) LogOnlineState(id string, online bool) {
}

View File

@@ -5,6 +5,7 @@ import (
"net" "net"
"strings" "strings"
"github.com/InazumaV/V2bX/common/format"
"github.com/InazumaV/V2bX/limiter" "github.com/InazumaV/V2bX/limiter"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
@@ -46,12 +47,18 @@ var logFormatMap = map[string]zapcore.EncoderConfig{
} }
func (l *serverLogger) Connect(addr net.Addr, uuid string, tx uint64) { func (l *serverLogger) Connect(addr net.Addr, uuid string, tx uint64) {
limiter, err := limiter.GetLimiter(l.Tag) limiterinfo, err := limiter.GetLimiter(l.Tag)
if err != nil { if err != nil {
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err)) l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
} }
if _, r := limiter.CheckLimit(uuid, extractIPFromAddr(addr), addr.Network() == "tcp"); r { if _, r := limiterinfo.CheckLimit(format.UserTag(l.Tag, uuid), extractIPFromAddr(addr), addr.Network() == "tcp", true); r {
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid)) if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
userLimit.(*limiter.UserLimitInfo).OverLimit = true
}
} else {
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
userLimit.(*limiter.UserLimitInfo).OverLimit = false
}
} }
l.logger.Info("client connected", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint64("tx", tx)) l.logger.Info("client connected", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint64("tx", tx))
} }
@@ -61,12 +68,18 @@ func (l *serverLogger) Disconnect(addr net.Addr, uuid string, err error) {
} }
func (l *serverLogger) TCPRequest(addr net.Addr, uuid, reqAddr string) { func (l *serverLogger) TCPRequest(addr net.Addr, uuid, reqAddr string) {
limiter, err := limiter.GetLimiter(l.Tag) limiterinfo, err := limiter.GetLimiter(l.Tag)
if err != nil { if err != nil {
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err)) l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
} }
if _, r := limiter.CheckLimit(uuid, extractIPFromAddr(addr), addr.Network() == "tcp"); r { if _, r := limiterinfo.CheckLimit(format.UserTag(l.Tag, uuid), extractIPFromAddr(addr), addr.Network() == "tcp", true); r {
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid)) if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
userLimit.(*limiter.UserLimitInfo).OverLimit = true
}
} else {
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
userLimit.(*limiter.UserLimitInfo).OverLimit = false
}
} }
l.logger.Debug("TCP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.String("reqAddr", reqAddr)) l.logger.Debug("TCP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.String("reqAddr", reqAddr))
} }
@@ -80,12 +93,18 @@ func (l *serverLogger) TCPError(addr net.Addr, uuid, reqAddr string, err error)
} }
func (l *serverLogger) UDPRequest(addr net.Addr, uuid string, sessionId uint32, reqAddr string) { func (l *serverLogger) UDPRequest(addr net.Addr, uuid string, sessionId uint32, reqAddr string) {
limiter, err := limiter.GetLimiter(l.Tag) limiterinfo, err := limiter.GetLimiter(l.Tag)
if err != nil { if err != nil {
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err)) l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
} }
if _, r := limiter.CheckLimit(uuid, extractIPFromAddr(addr), addr.Network() == "tcp"); r { if _, r := limiterinfo.CheckLimit(format.UserTag(l.Tag, uuid), extractIPFromAddr(addr), addr.Network() == "tcp", true); r {
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid)) if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
userLimit.(*limiter.UserLimitInfo).OverLimit = true
}
} else {
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
userLimit.(*limiter.UserLimitInfo).OverLimit = false
}
} }
l.logger.Debug("UDP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint32("sessionId", sessionId), zap.String("reqAddr", reqAddr)) l.logger.Debug("UDP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint32("sessionId", sessionId), zap.String("reqAddr", reqAddr))
} }

View File

@@ -1,11 +1,11 @@
package hy2 package hy2
import ( import (
"fmt" "strings"
"github.com/InazumaV/V2bX/api/panel" "github.com/InazumaV/V2bX/api/panel"
"github.com/InazumaV/V2bX/conf" "github.com/InazumaV/V2bX/conf"
"github.com/apernet/hysteria/core/server" "github.com/apernet/hysteria/core/v2/server"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -31,8 +31,6 @@ func (h *Hysteria2) AddNode(tag string, info *panel.NodeInfo, config *conf.Optio
if err := v.Unmarshal(&c); err != nil { if err := v.Unmarshal(&c); err != nil {
h.Logger.Fatal("failed to parse server config", zap.Error(err)) h.Logger.Fatal("failed to parse server config", zap.Error(err))
} }
h.Logger.Debug("loaded server config:")
fmt.Printf("%+v", c)
} }
n := Hysteria2node{ n := Hysteria2node{
Tag: tag, Tag: tag,
@@ -42,11 +40,12 @@ func (h *Hysteria2) AddNode(tag string, info *panel.NodeInfo, config *conf.Optio
logger: h.Logger, logger: h.Logger,
}, },
TrafficLogger: &HookServer{ TrafficLogger: &HookServer{
Tag: tag, Tag: tag,
logger: h.Logger,
}, },
} }
hyconfig, err = n.getHyConfig(tag, info, config, &c) hyconfig, err = n.getHyConfig(info, config, &c)
if err != nil { if err != nil {
return err return err
} }
@@ -59,7 +58,9 @@ func (h *Hysteria2) AddNode(tag string, info *panel.NodeInfo, config *conf.Optio
h.Hy2nodes[tag] = n h.Hy2nodes[tag] = n
go func() { go func() {
if err := s.Serve(); err != nil { if err := s.Serve(); err != nil {
h.Logger.Error("Server Error", zap.Error(err)) if !strings.Contains(err.Error(), "quic: server closed") {
h.Logger.Error("Server Error", zap.Error(err))
}
} }
}() }()
return nil return nil

View File

@@ -6,7 +6,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/apernet/hysteria/extras/outbounds" "github.com/apernet/hysteria/extras/v2/outbounds"
) )
type serverConfig struct { type serverConfig struct {

View File

@@ -7,7 +7,7 @@ import (
"github.com/InazumaV/V2bX/api/panel" "github.com/InazumaV/V2bX/api/panel"
"github.com/InazumaV/V2bX/common/counter" "github.com/InazumaV/V2bX/common/counter"
vCore "github.com/InazumaV/V2bX/core" vCore "github.com/InazumaV/V2bX/core"
"github.com/apernet/hysteria/core/server" "github.com/apernet/hysteria/core/v2/server"
) )
var _ server.Authenticator = &V2bX{} var _ server.Authenticator = &V2bX{}

View File

@@ -8,6 +8,7 @@ import (
"github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing-box/common/urltest"
"github.com/InazumaV/V2bX/common/format"
"github.com/InazumaV/V2bX/common/rate" "github.com/InazumaV/V2bX/common/rate"
"github.com/InazumaV/V2bX/limiter" "github.com/InazumaV/V2bX/limiter"
@@ -16,9 +17,14 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
) )
var _ adapter.ClashServer = (*HookServer)(nil)
type HookServer struct { type HookServer struct {
ctx context.Context
urlTestHistory *urltest.HistoryStorage
EnableConnClear bool EnableConnClear bool
counter sync.Map counter sync.Map
connClears sync.Map connClears sync.Map
@@ -55,12 +61,18 @@ func (h *HookServer) ModeList() []string {
return nil return nil
} }
func NewHookServer(enableClear bool) *HookServer { func NewHookServer(ctx context.Context, enableClear bool) *HookServer {
return &HookServer{ server := &HookServer{
ctx: ctx,
EnableConnClear: enableClear, EnableConnClear: enableClear,
counter: sync.Map{}, counter: sync.Map{},
connClears: sync.Map{}, connClears: sync.Map{},
} }
server.urlTestHistory = service.PtrFromContext[urltest.HistoryStorage](ctx)
if server.urlTestHistory == nil {
server.urlTestHistory = urltest.NewHistoryStorage()
}
return server
} }
func (h *HookServer) Start() error { func (h *HookServer) Start() error {
@@ -68,6 +80,7 @@ func (h *HookServer) Start() error {
} }
func (h *HookServer) Close() error { func (h *HookServer) Close() error {
h.urlTestHistory.Close()
return nil return nil
} }
@@ -95,7 +108,7 @@ func (h *HookServer) RoutedConnection(_ context.Context, conn net.Conn, m adapte
return conn, t return conn, t
} }
ip := m.Source.Addr.String() ip := m.Source.Addr.String()
if b, r := l.CheckLimit(m.User, ip, true); r { if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, true, true); r {
conn.Close() conn.Close()
log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn") log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn")
return conn, t return conn, t
@@ -149,12 +162,12 @@ func (h *HookServer) RoutedPacketConnection(_ context.Context, conn N.PacketConn
return conn, t return conn, t
} }
ip := m.Source.Addr.String() ip := m.Source.Addr.String()
if b, r := l.CheckLimit(m.User, ip, true); r { if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, false, false); r {
conn.Close() conn.Close()
log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn") log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn")
return conn, t return conn, t
} else if b != nil { } else if b != nil {
conn = rate.NewPacketConnCounter(conn, b) //conn = rate.NewPacketConnCounter(conn, b)
} }
if h.EnableConnClear { if h.EnableConnClear {
var key int var key int
@@ -192,7 +205,7 @@ func (h *HookServer) CacheFile() adapter.CacheFile {
return nil return nil
} }
func (h *HookServer) HistoryStorage() *urltest.HistoryStorage { func (h *HookServer) HistoryStorage() *urltest.HistoryStorage {
return nil return h.urlTestHistory
} }
func (h *HookServer) StoreFakeIP() bool { func (h *HookServer) StoreFakeIP() bool {

View File

@@ -40,6 +40,15 @@ type WsNetworkConfig struct {
Headers map[string]string `json:"headers"` Headers map[string]string `json:"headers"`
} }
type GrpcNetworkConfig struct {
ServiceName string `json:"serviceName"`
}
type HttpupgradeNetworkConfig struct {
Path string `json:"path"`
Host string `json:"host"`
}
func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (option.Inbound, error) { func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (option.Inbound, error) {
addr, err := netip.ParseAddr(c.ListenIP) addr, err := netip.ParseAddr(c.ListenIP)
if err != nil { if err != nil {
@@ -91,7 +100,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
Enabled: true, Enabled: true,
ShortID: []string{v.TlsSettings.ShortId}, ShortID: []string{v.TlsSettings.ShortId},
PrivateKey: v.TlsSettings.PrivateKey, PrivateKey: v.TlsSettings.PrivateKey,
Xver: v.TlsSettings.Xver, Xver: uint8(v.TlsSettings.Xver),
Handshake: option.InboundRealityHandshakeOptions{ Handshake: option.InboundRealityHandshakeOptions{
ServerOptions: option.ServerOptions{ ServerOptions: option.ServerOptions{
Server: dest, Server: dest,
@@ -170,12 +179,28 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
Headers: headers, Headers: headers,
} }
case "grpc": case "grpc":
network := GrpcNetworkConfig{}
if len(n.NetworkSettings) != 0 { if len(n.NetworkSettings) != 0 {
err := json.Unmarshal(n.NetworkSettings, &t.GRPCOptions) err := json.Unmarshal(n.NetworkSettings, &network)
if err != nil { if err != nil {
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err) return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
} }
} }
t.GRPCOptions = option.V2RayGRPCOptions{
ServiceName: network.ServiceName,
}
case "httpupgrade":
network := HttpupgradeNetworkConfig{}
if len(n.NetworkSettings) != 0 {
err := json.Unmarshal(n.NetworkSettings, &network)
if err != nil {
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
}
}
t.HTTPUpgradeOptions = option.V2RayHTTPUpgradeOptions{
Path: network.Path,
Host: network.Host,
}
} }
if info.Type == "vless" { if info.Type == "vless" {
in.Type = "vless" in.Type = "vless"
@@ -203,7 +228,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
switch n.Cipher { switch n.Cipher {
case "2022-blake3-aes-128-gcm": case "2022-blake3-aes-128-gcm":
keyLength = 16 keyLength = 16
case "2022-blake3-aes-256-gcm": case "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305":
keyLength = 32 keyLength = 32
default: default:
keyLength = 16 keyLength = 16
@@ -263,12 +288,16 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
Headers: headers, Headers: headers,
} }
case "grpc": case "grpc":
network := GrpcNetworkConfig{}
if len(n.NetworkSettings) != 0 { if len(n.NetworkSettings) != 0 {
err := json.Unmarshal(n.NetworkSettings, &t.GRPCOptions) err := json.Unmarshal(n.NetworkSettings, &network)
if err != nil { if err != nil {
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err) return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
} }
} }
t.GRPCOptions = option.V2RayGRPCOptions{
ServiceName: network.ServiceName,
}
default: default:
t.Type = "" t.Type = ""
} }
@@ -348,6 +377,7 @@ func (b *Sing) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) e
b.ctx, b.ctx,
b.box.Router(), b.box.Router(),
b.logFactory.NewLogger(F.ToString("inbound/", c.Type, "[", tag, "]")), b.logFactory.NewLogger(F.ToString("inbound/", c.Type, "[", tag, "]")),
tag,
c, c,
nil, nil,
) )

View File

@@ -3,7 +3,6 @@ package sing
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"os" "os"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
@@ -57,33 +56,12 @@ func New(c *conf.CoreConfig) (vCore.Core, error) {
options.NTP = &option.NTPOptions{ options.NTP = &option.NTPOptions{
Enabled: c.SingConfig.NtpConfig.Enable, Enabled: c.SingConfig.NtpConfig.Enable,
WriteToSystem: true, WriteToSystem: true,
Server: c.SingConfig.NtpConfig.Server, ServerOptions: option.ServerOptions{
ServerPort: c.SingConfig.NtpConfig.ServerPort, Server: c.SingConfig.NtpConfig.Server,
ServerPort: c.SingConfig.NtpConfig.ServerPort,
},
} }
os.Setenv("SING_DNS_PATH", "") os.Setenv("SING_DNS_PATH", "")
options.DNS = &option.DNSOptions{}
if c.SingConfig.DnsConfigPath != "" {
f, err := os.OpenFile(c.SingConfig.DnsConfigPath, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return nil, fmt.Errorf("failed to open or create sing dns config file: %s", err)
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
log.Warn(fmt.Sprintf(
"Failed to read sing dns config from file '%v': %v. Using default DNS options",
f.Name(), err))
options.DNS = &option.DNSOptions{}
} else {
if err := json.Unmarshal(data, options.DNS); err != nil {
log.Warn(fmt.Sprintf(
"Failed to unmarshal sing dns config from file '%v': %v. Using default DNS options",
f.Name(), err))
options.DNS = &option.DNSOptions{}
}
}
os.Setenv("SING_DNS_PATH", c.SingConfig.DnsConfigPath)
}
b, err := box.New(box.Options{ b, err := box.New(box.Options{
Context: context.Background(), Context: context.Background(),
Options: options, Options: options,
@@ -91,7 +69,7 @@ func New(c *conf.CoreConfig) (vCore.Core, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
hs := NewHookServer(c.SingConfig.EnableConnClear) hs := NewHookServer(b.Router().GetCtx(), c.SingConfig.EnableConnClear)
b.Router().SetClashServer(hs) b.Router().SetClashServer(hs)
return &Sing{ return &Sing{
ctx: b.Router().GetCtx(), ctx: b.Router().GetCtx(),

View File

@@ -5,6 +5,7 @@ package dispatcher
import ( import (
"context" "context"
"fmt" "fmt"
"regexp"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -14,6 +15,7 @@ import (
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
@@ -29,7 +31,7 @@ import (
"github.com/xtls/xray-core/transport/pipe" "github.com/xtls/xray-core/transport/pipe"
) )
var errSniffingTimeout = newError("timeout on sniffing") var errSniffingTimeout = errors.New("timeout on sniffing")
type cachedReader struct { type cachedReader struct {
sync.Mutex sync.Mutex
@@ -157,6 +159,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network) (*
var user *protocol.MemoryUser var user *protocol.MemoryUser
if sessionInbound != nil { if sessionInbound != nil {
user = sessionInbound.User user = sessionInbound.User
sessionInbound.CanSpliceCopy = 3
} }
var limit *limiter.Limiter var limit *limiter.Limiter
@@ -164,24 +167,25 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network) (*
if user != nil && len(user.Email) > 0 { if user != nil && len(user.Email) > 0 {
limit, err = limiter.GetLimiter(sessionInbound.Tag) limit, err = limiter.GetLimiter(sessionInbound.Tag)
if err != nil { if err != nil {
newError("get limiter ", sessionInbound.Tag, " error: ", err).AtError().WriteToLog() errors.LogInfo(ctx, "get limiter ", sessionInbound.Tag, " error: ", err)
common.Close(outboundLink.Writer) common.Close(outboundLink.Writer)
common.Close(inboundLink.Writer) common.Close(inboundLink.Writer)
common.Interrupt(outboundLink.Reader) common.Interrupt(outboundLink.Reader)
common.Interrupt(inboundLink.Reader) common.Interrupt(inboundLink.Reader)
return nil, nil, nil, newError("get limiter ", sessionInbound.Tag, " error: ", err) return nil, nil, nil, errors.New("get limiter ", sessionInbound.Tag, " error: ", err)
} }
// Speed Limit and Device Limit // Speed Limit and Device Limit
w, reject := limit.CheckLimit(user.Email, w, reject := limit.CheckLimit(user.Email,
sessionInbound.Source.Address.IP().String(), sessionInbound.Source.Address.IP().String(),
network == net.Network_TCP) network == net.Network_TCP,
sessionInbound.Source.Network == net.Network_TCP)
if reject { if reject {
newError("Limited ", user.Email, " by conn or ip").AtWarning().WriteToLog() errors.LogInfo(ctx, "Limited ", user.Email, " by conn or ip")
common.Close(outboundLink.Writer) common.Close(outboundLink.Writer)
common.Close(inboundLink.Writer) common.Close(inboundLink.Writer)
common.Interrupt(outboundLink.Reader) common.Interrupt(outboundLink.Reader)
common.Interrupt(inboundLink.Reader) common.Interrupt(inboundLink.Reader)
return nil, nil, nil, newError("Limited ", user.Email, " by conn or ip") return nil, nil, nil, errors.New("Limited ", user.Email, " by conn or ip")
} }
if w != nil { if w != nil {
inboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w) inboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w)
@@ -217,8 +221,20 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
return false return false
} }
for _, d := range request.ExcludeForDomain { for _, d := range request.ExcludeForDomain {
if strings.ToLower(domain) == d { if strings.HasPrefix(d, "regexp:") {
return false pattern := d[7:]
re, err := regexp.Compile(pattern)
if err != nil {
errors.LogInfo(ctx, "Unable to compile regex")
continue
}
if re.MatchString(domain) {
return false
}
} else {
if strings.ToLower(domain) == d {
return false
}
} }
} }
protocolString := result.Protocol() protocolString := result.Protocol()
@@ -226,12 +242,12 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
protocolString = resComp.ProtocolForDomainResult() protocolString = resComp.ProtocolForDomainResult()
} }
for _, p := range request.OverrideDestinationForProtocol { for _, p := range request.OverrideDestinationForProtocol {
if strings.HasPrefix(protocolString, p) || strings.HasPrefix(protocolString, p) { if strings.HasPrefix(protocolString, p) || strings.HasPrefix(p, protocolString) {
return true return true
} }
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" && if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) { destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "Using sniffer ", protocolString, " since the fake DNS missed")
return true return true
} }
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok { if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
@@ -249,11 +265,14 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
if !destination.IsValid() { if !destination.IsValid() {
panic("Dispatcher: Invalid destination.") panic("Dispatcher: Invalid destination.")
} }
ob := &session.Outbound{ outbounds := session.OutboundsFromContext(ctx)
OriginalTarget: destination, if len(outbounds) == 0 {
Target: destination, outbounds = []*session.Outbound{{}}
ctx = session.ContextWithOutbounds(ctx, outbounds)
} }
ctx = session.ContextWithOutbound(ctx, ob) ob := outbounds[len(outbounds)-1]
ob.OriginalTarget = destination
ob.Target = destination
content := session.ContentFromContext(ctx) content := session.ContentFromContext(ctx)
if content == nil { if content == nil {
content = new(session.Content) content = new(session.Content)
@@ -278,7 +297,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
} }
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) { if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain() domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "sniffed domain: ", domain)
destination.Address = net.ParseAddress(domain) destination.Address = net.ParseAddress(domain)
protocol := result.Protocol() protocol := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok { if resComp, ok := result.(SnifferResultComposite); ok {
@@ -303,13 +322,16 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
// DispatchLink implements routing.Dispatcher. // DispatchLink implements routing.Dispatcher.
func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error { func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error {
if !destination.IsValid() { if !destination.IsValid() {
return newError("Dispatcher: Invalid destination.") return errors.New("Dispatcher: Invalid destination.")
} }
ob := &session.Outbound{ outbounds := session.OutboundsFromContext(ctx)
OriginalTarget: destination, if len(outbounds) == 0 {
Target: destination, outbounds = []*session.Outbound{{}}
ctx = session.ContextWithOutbounds(ctx, outbounds)
} }
ctx = session.ContextWithOutbound(ctx, ob) ob := outbounds[len(outbounds)-1]
ob.OriginalTarget = destination
ob.Target = destination
content := session.ContentFromContext(ctx) content := session.ContentFromContext(ctx)
if content == nil { if content == nil {
content = new(session.Content) content = new(session.Content)
@@ -329,7 +351,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
} }
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) { if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain() domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "sniffed domain: ", domain)
destination.Address = net.ParseAddress(domain) destination.Address = net.ParseAddress(domain)
protocol := result.Protocol() protocol := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok { if resComp, ok := result.(SnifferResultComposite); ok {
@@ -399,7 +421,8 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
} }
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination, l *limiter.Limiter, protocol string) { func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination, l *limiter.Limiter, protocol string) {
ob := session.OutboundFromContext(ctx) outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() { if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
proxied := hosts.LookupHosts(ob.Target.String()) proxied := hosts.LookupHosts(ob.Target.String())
if proxied != nil { if proxied != nil {
@@ -426,7 +449,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
var err error var err error
l, err = limiter.GetLimiter(sessionInbound.Tag) l, err = limiter.GetLimiter(sessionInbound.Tag)
if err != nil { if err != nil {
newError("get limiter ", sessionInbound.Tag, " error: ", err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) errors.LogError(ctx, "get limiter ", sessionInbound.Tag, " error: ", err)
} }
} }
if l != nil { if l != nil {
@@ -437,20 +460,20 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
destStr = destination.Address.IP().String() destStr = destination.Address.IP().String()
} }
if l.CheckDomainRule(destStr) { if l.CheckDomainRule(destStr) {
newError(fmt.Sprintf( errors.LogError(ctx, fmt.Sprintf(
"User %s access domain %s reject by rule", "User %s access domain %s reject by rule",
sessionInbound.User.Email, sessionInbound.User.Email,
destStr)).AtWarning().WriteToLog(session.ExportIDToError(ctx)) destStr))
common.Close(link.Writer) common.Close(link.Writer)
common.Interrupt(link.Reader) common.Interrupt(link.Reader)
return return
} }
if len(protocol) != 0 { if len(protocol) != 0 {
if l.CheckProtocolRule(protocol) { if l.CheckProtocolRule(protocol) {
newError(fmt.Sprintf( errors.LogError(ctx, fmt.Sprintf(
"User %s access protocol %s reject by rule", "User %s access protocol %s reject by rule",
sessionInbound.User.Email, sessionInbound.User.Email,
protocol)).AtWarning().WriteToLog(session.ExportIDToError(ctx)) protocol))
common.Close(link.Writer) common.Close(link.Writer)
common.Interrupt(link.Reader) common.Interrupt(link.Reader)
return return
@@ -468,10 +491,10 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
ctx = session.SetForcedOutboundTagToContext(ctx, "") ctx = session.SetForcedOutboundTagToContext(ctx, "")
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil { if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
isPickRoute = 1 isPickRoute = 1
newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]")
handler = h handler = h
} else { } else {
newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx)) errors.LogError(ctx, "non existing tag for platform initialized detour: ", forcedOutboundTag)
common.Close(link.Writer) common.Close(link.Writer)
common.Interrupt(link.Reader) common.Interrupt(link.Reader)
return return
@@ -481,13 +504,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
outTag := route.GetOutboundTag() outTag := route.GetOutboundTag()
if h := d.ohm.GetHandler(outTag); h != nil { if h := d.ohm.GetHandler(outTag); h != nil {
isPickRoute = 2 isPickRoute = 2
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
handler = h handler = h
} else { } else {
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) errors.LogWarning(ctx, "non existing outTag: ", outTag)
} }
} else { } else {
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "default route for ", destination)
} }
} }
@@ -500,7 +523,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
} }
if handler == nil { if handler == nil {
newError("default outbound handler not exist").WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "default outbound handler not exist")
common.Close(link.Writer) common.Close(link.Writer)
common.Interrupt(link.Reader) common.Interrupt(link.Reader)
return return

View File

@@ -1,9 +0,0 @@
package dispatcher
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

View File

@@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
@@ -22,15 +23,16 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
} }
if fakeDNSEngine == nil { if fakeDNSEngine == nil {
errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError() errNotInit := errors.New("FakeDNSEngine is not initialized, but such a sniffer is used").AtError()
return protocolSnifferWithMetadata{}, errNotInit return protocolSnifferWithMetadata{}, errNotInit
} }
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) { return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
Target := session.OutboundFromContext(ctx).Target outbounds := session.OutboundsFromContext(ctx)
if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP { ob := outbounds[len(outbounds)-1]
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address) if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP {
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address)
if domainFromFakeDNS != "" { if domainFromFakeDNS != "" {
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx)) errors.LogInfo(ctx, "fake dns got domain: ", domainFromFakeDNS, " for ip: ", ob.Target.Address.String())
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
} }
} }
@@ -38,7 +40,7 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil { if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt) ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok { if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
inPool := fkr0.IsIPInIPPool(Target.Address) inPool := fkr0.IsIPInIPPool(ob.Target.Address)
ipAddressInRangeValue.addressInRange = &inPool ipAddressInRangeValue.addressInRange = &inPool
} }
} }
@@ -108,10 +110,10 @@ func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWit
} }
return nil, common.ErrNoClue return nil, common.ErrNoClue
} }
newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog() errors.LogDebug(ctx, "ip address not in fake dns range, return as is")
return nil, common.ErrNoClue return nil, common.ErrNoClue
} }
newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog() errors.LogWarning(ctx, "fake dns sniffer did not set address in range option, assume false.")
return nil, common.ErrNoClue return nil, common.ErrNoClue
}, },
metadataSniffer: false, metadataSniffer: false,

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/bittorrent" "github.com/xtls/xray-core/common/protocol/bittorrent"
"github.com/xtls/xray-core/common/protocol/http" "github.com/xtls/xray-core/common/protocol/http"
@@ -52,7 +53,7 @@ func NewSniffer(ctx context.Context) *Sniffer {
return ret return ret
} }
var errUnknownContent = newError("unknown content") var errUnknownContent = errors.New("unknown content")
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) { func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
var pendingSniffer []protocolSnifferWithMetadata var pendingSniffer []protocolSnifferWithMetadata

View File

@@ -26,8 +26,12 @@ func buildInbound(option *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*
err = buildV2ray(option, nodeInfo, in) err = buildV2ray(option, nodeInfo, in)
network = nodeInfo.VAllss.Network network = nodeInfo.VAllss.Network
case "trojan": case "trojan":
err = buildTrojan(option, in) err = buildTrojan(option, nodeInfo, in)
network = "tcp" if nodeInfo.Trojan.Network != "" {
network = nodeInfo.Trojan.Network
} else {
network = "tcp"
}
case "shadowsocks": case "shadowsocks":
err = buildShadowsocks(option, nodeInfo, in) err = buildShadowsocks(option, nodeInfo, in)
network = "tcp" network = "tcp"
@@ -69,8 +73,13 @@ func buildInbound(option *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*
in.StreamSetting.TCPSettings = tcpSetting in.StreamSetting.TCPSettings = tcpSetting
} }
case "ws": case "ws":
in.StreamSetting.WSSettings = &coreConf.WebSocketConfig{ if in.StreamSetting.WSSettings != nil {
AcceptProxyProtocol: option.XrayOptions.EnableProxyProtocol} //Enable proxy protocol in.StreamSetting.WSSettings.AcceptProxyProtocol = option.XrayOptions.EnableProxyProtocol
} else {
in.StreamSetting.WSSettings = &coreConf.WebSocketConfig{
AcceptProxyProtocol: option.XrayOptions.EnableProxyProtocol,
} //Enable proxy protocol
}
default: default:
socketConfig := &coreConf.SocketConfig{ socketConfig := &coreConf.SocketConfig{
AcceptProxyProtocol: option.XrayOptions.EnableProxyProtocol, AcceptProxyProtocol: option.XrayOptions.EnableProxyProtocol,
@@ -105,9 +114,17 @@ func buildInbound(option *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*
// Reality // Reality
in.StreamSetting.Security = "reality" in.StreamSetting.Security = "reality"
v := nodeInfo.VAllss v := nodeInfo.VAllss
dest := v.TlsSettings.Dest
if dest == "" {
dest = v.TlsSettings.ServerName
}
xver := v.TlsSettings.Xver
if xver == 0 {
xver = v.RealityConfig.Xver
}
d, err := json.Marshal(fmt.Sprintf( d, err := json.Marshal(fmt.Sprintf(
"%s:%s", "%s:%s",
v.TlsSettings.ServerName, dest,
v.TlsSettings.ServerPort)) v.TlsSettings.ServerPort))
if err != nil { if err != nil {
return nil, fmt.Errorf("marshal reality dest error: %s", err) return nil, fmt.Errorf("marshal reality dest error: %s", err)
@@ -115,7 +132,7 @@ func buildInbound(option *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*
mtd, _ := time.ParseDuration(v.RealityConfig.MaxTimeDiff) mtd, _ := time.ParseDuration(v.RealityConfig.MaxTimeDiff)
in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{ in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{
Dest: d, Dest: d,
Xver: v.RealityConfig.Xver, Xver: xver,
ServerNames: []string{v.TlsSettings.ServerName}, ServerNames: []string{v.TlsSettings.ServerName},
PrivateKey: v.TlsSettings.PrivateKey, PrivateKey: v.TlsSettings.PrivateKey,
MinClientVer: v.RealityConfig.MinClientVer, MinClientVer: v.RealityConfig.MinClientVer,
@@ -123,6 +140,7 @@ func buildInbound(option *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*
MaxTimeDiff: uint64(mtd.Microseconds()), MaxTimeDiff: uint64(mtd.Microseconds()),
ShortIds: []string{v.TlsSettings.ShortId}, ShortIds: []string{v.TlsSettings.ShortId},
} }
default:
break break
} }
in.Tag = tag in.Tag = tag
@@ -172,9 +190,77 @@ func buildV2ray(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreCon
return nil return nil
} }
t := coreConf.TransportProtocol(nodeInfo.VAllss.Network) t := coreConf.TransportProtocol(v.Network)
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t} inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
switch v.Network { switch v.Network {
case "tcp":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.TCPSettings)
if err != nil {
return fmt.Errorf("unmarshal tcp settings error: %s", err)
}
case "ws":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.WSSettings)
if err != nil {
return fmt.Errorf("unmarshal ws settings error: %s", err)
}
case "grpc":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.GRPCConfig)
if err != nil {
return fmt.Errorf("unmarshal grpc settings error: %s", err)
}
case "http":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.HTTPSettings)
if err != nil {
return fmt.Errorf("unmarshal grpc settings error: %s", err)
}
case "quic":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.QUICSettings)
if err != nil {
return fmt.Errorf("unmarshal grpc settings error: %s", err)
}
case "httpupgrade":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.HTTPUPGRADESettings)
if err != nil {
return fmt.Errorf("unmarshal httpupgrade settings error: %s", err)
}
case "splithttp":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.SplitHTTPSettings)
if err != nil {
return fmt.Errorf("unmarshal splithttp settings error: %s", err)
}
default:
return errors.New("the network type is not vail")
}
return nil
}
func buildTrojan(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
inbound.Protocol = "trojan"
v := nodeInfo.Trojan
if config.XrayOptions.EnableFallback {
// Set fallback
fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)
if err != nil {
return err
}
s, err := json.Marshal(&coreConf.TrojanServerConfig{
Fallbacks: fallbackConfigs,
})
inbound.Settings = (*json.RawMessage)(&s)
if err != nil {
return fmt.Errorf("marshal trojan fallback config error: %s", err)
}
} else {
s := []byte("{}")
inbound.Settings = (*json.RawMessage)(&s)
}
network := v.Network
if network == "" {
network = "tcp"
}
t := coreConf.TransportProtocol(network)
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
switch network {
case "tcp": case "tcp":
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.TCPSettings) err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.TCPSettings)
if err != nil { if err != nil {
@@ -196,30 +282,6 @@ func buildV2ray(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreCon
return nil return nil
} }
func buildTrojan(config *conf.Options, inbound *coreConf.InboundDetourConfig) error {
inbound.Protocol = "trojan"
if config.XrayOptions.EnableFallback {
// Set fallback
fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)
if err != nil {
return err
}
s, err := json.Marshal(&coreConf.TrojanServerConfig{
Fallbacks: fallbackConfigs,
})
inbound.Settings = (*json.RawMessage)(&s)
if err != nil {
return fmt.Errorf("marshal trojan fallback config error: %s", err)
}
} else {
s := []byte("{}")
inbound.Settings = (*json.RawMessage)(&s)
}
t := coreConf.TransportProtocol("tcp")
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
return nil
}
func buildShadowsocks(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { func buildShadowsocks(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
inbound.Protocol = "shadowsocks" inbound.Protocol = "shadowsocks"
s := nodeInfo.Shadowsocks s := nodeInfo.Shadowsocks

View File

@@ -5,7 +5,6 @@ import (
conf2 "github.com/InazumaV/V2bX/conf" conf2 "github.com/InazumaV/V2bX/conf"
"github.com/goccy/go-json" "github.com/goccy/go-json"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf" "github.com/xtls/xray-core/infra/conf"
) )
@@ -18,8 +17,7 @@ func buildOutbound(config *conf2.Options, tag string) (*core.OutboundHandlerConf
// Build Send IP address // Build Send IP address
if config.SendIP != "" { if config.SendIP != "" {
ipAddress := net.ParseAddress(config.SendIP) outboundDetourConfig.SendThrough = &config.SendIP
outboundDetourConfig.SendThrough = &conf.Address{Address: ipAddress}
} }
// Freedom Protocol setting // Freedom Protocol setting

View File

@@ -38,9 +38,13 @@ func buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey
keyLength = 16 keyLength = 16
case "2022-blake3-aes-256-gcm": case "2022-blake3-aes-256-gcm":
keyLength = 32 keyLength = 32
case "2022-blake3-chacha20-poly1305":
keyLength = 32
} }
ssAccount := &shadowsocks_2022.User{ ssAccount := &shadowsocks_2022.User{
Key: base64.StdEncoding.EncodeToString([]byte(userInfo.Uuid[:keyLength])), Level: 0,
Email: format.UserTag(tag, userInfo.Uuid),
Key: base64.StdEncoding.EncodeToString([]byte(userInfo.Uuid[:keyLength])),
} }
return &protocol.User{ return &protocol.User{
Level: 0, Level: 0,

256
go.mod
View File

@@ -2,105 +2,125 @@ module github.com/InazumaV/V2bX
go 1.22 go 1.22
toolchain go1.22.0 toolchain go1.22.5
require ( require (
github.com/apernet/hysteria/core v1.3.5-0.20240201034858-bb99579bb92c github.com/apernet/hysteria/core/v2 v2.5.2-0.20240825173017-21ea2a024a5b
github.com/apernet/hysteria/extras v0.0.0-20240201034858-bb99579bb92c github.com/apernet/hysteria/extras/v2 v2.5.2-0.20240825173017-21ea2a024a5b
github.com/beevik/ntp v1.2.0 github.com/beevik/ntp v1.2.0
github.com/fsnotify/fsnotify v1.7.0 github.com/fsnotify/fsnotify v1.7.0
github.com/go-acme/lego/v4 v4.13.2 github.com/go-acme/lego/v4 v4.17.4
github.com/go-resty/resty/v2 v2.7.0 github.com/go-resty/resty/v2 v2.13.1
github.com/goccy/go-json v0.10.2 github.com/goccy/go-json v0.10.3
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/juju/ratelimit v1.0.2 github.com/juju/ratelimit v1.0.2
github.com/sagernet/sing v0.3.2 github.com/sagernet/sing v0.5.0-beta.1
github.com/sagernet/sing-box v1.9.0 github.com/sagernet/sing-box v1.10.0-beta.5
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.15.0 github.com/spf13/viper v1.15.0
github.com/xtls/xray-core v1.8.8 github.com/xtls/xray-core v1.8.24
go.uber.org/zap v1.26.0 go.uber.org/zap v1.27.0
golang.org/x/crypto v0.19.0 golang.org/x/crypto v0.26.0
golang.org/x/sys v0.17.0 golang.org/x/sys v0.24.0
google.golang.org/protobuf v1.32.0 google.golang.org/protobuf v1.34.2
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
) )
require ( require (
berty.tech/go-libtor v1.0.385 // indirect berty.tech/go-libtor v1.0.385 // indirect
cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.24 // indirect github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.62.712 // indirect
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f // indirect github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 // indirect
github.com/aws/aws-sdk-go v1.39.0 // indirect github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.18 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.18 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.38.3 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/caddyserver/certmagic v0.20.0 // indirect github.com/caddyserver/certmagic v0.20.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/civo/civogo v0.3.11 // indirect github.com/civo/civogo v0.3.11 // indirect
github.com/cloudflare/circl v1.3.7 // indirect github.com/cloudflare/circl v1.4.0 // indirect
github.com/cloudflare/cloudflare-go v0.70.0 // indirect github.com/cloudflare/cloudflare-go v0.97.0 // indirect
github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpu/goacmedns v0.1.1 // indirect
github.com/cretz/bine v0.2.0 // indirect github.com/cretz/bine v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deepmap/oapi-codegen v1.9.1 // indirect github.com/deepmap/oapi-codegen v1.9.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dnsimple/dnsimple-go v1.2.0 // indirect github.com/dnsimple/dnsimple-go v1.7.0 // indirect
github.com/exoscale/egoscale v0.100.1 // indirect github.com/exoscale/egoscale v0.102.3 // indirect
github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect github.com/francoispqt/gojay v1.2.13 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect github.com/gaukas/godicttls v0.0.4 // indirect
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/go-chi/chi/v5 v5.0.12 // indirect
github.com/go-errors/errors v1.0.1 // indirect github.com/go-errors/errors v1.0.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/pool v0.2.1 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/gofrs/flock v0.8.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gofrs/uuid/v5 v5.2.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20240225044709-fd706174c886 // indirect github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
github.com/google/s2a-go v0.1.7 // indirect github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/gophercloud/gophercloud v1.0.0 // indirect github.com/gophercloud/gophercloud v1.12.0 // indirect
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
github.com/gorilla/websocket v1.5.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect
@@ -108,28 +128,29 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect github.com/labbsr0x/goh v1.0.1 // indirect
github.com/libdns/alidns v1.0.3 // indirect github.com/libdns/alidns v1.0.3 // indirect
github.com/libdns/cloudflare v0.1.0 // indirect github.com/libdns/cloudflare v0.1.1 // indirect
github.com/libdns/libdns v0.2.1 // indirect github.com/libdns/libdns v0.2.2 // indirect
github.com/linode/linodego v1.17.2 // indirect github.com/linode/linodego v1.28.0 // indirect
github.com/liquidweb/go-lwApi v0.0.5 // indirect
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.3 // indirect github.com/liquidweb/liquidweb-go v1.6.4 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d // indirect
github.com/mholt/acmez v1.2.0 // indirect github.com/mholt/acmez v1.2.0 // indirect
github.com/miekg/dns v1.1.58 // indirect github.com/miekg/dns v1.1.62 // indirect
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -137,113 +158,120 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/nrdcg/auroradns v1.1.0 // indirect github.com/nrdcg/auroradns v1.1.0 // indirect
github.com/nrdcg/desec v0.7.0 // indirect github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
github.com/nrdcg/desec v0.8.0 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nrdcg/freemyip v0.2.0 // indirect github.com/nrdcg/freemyip v0.2.0 // indirect
github.com/nrdcg/goinwx v0.8.2 // indirect github.com/nrdcg/goinwx v0.10.0 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
github.com/nrdcg/namesilo v0.2.1 // indirect github.com/nrdcg/namesilo v0.2.1 // indirect
github.com/nrdcg/nodion v0.1.0 // indirect github.com/nrdcg/nodion v0.1.0 // indirect
github.com/nrdcg/porkbun v0.2.0 // indirect github.com/nrdcg/porkbun v0.3.0 // indirect
github.com/nzdjb/go-metaname v1.0.0 // indirect github.com/nzdjb/go-metaname v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect github.com/onsi/ginkgo/v2 v2.19.0 // indirect
github.com/ooni/go-libtor v1.1.8 // indirect github.com/ooni/go-libtor v1.1.8 // indirect
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/oracle/oci-go-sdk/v65 v65.63.1 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/ovh/go-ovh v1.4.1 // indirect github.com/ovh/go-ovh v1.5.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/otp v1.4.0 // indirect github.com/pquerna/otp v1.4.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/quic-go/quic-go v0.41.0 // indirect github.com/quic-go/quic-go v0.46.0 // indirect
github.com/refraction-networking/utls v1.6.3 // indirect github.com/refraction-networking/utls v1.6.7 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/sacloud/api-client-go v0.2.8 // indirect github.com/sacloud/api-client-go v0.2.10 // indirect
github.com/sacloud/go-http v0.1.6 // indirect github.com/sacloud/go-http v0.1.8 // indirect
github.com/sacloud/iaas-api-go v1.11.1 // indirect github.com/sacloud/iaas-api-go v1.12.0 // indirect
github.com/sacloud/packages-go v0.0.9 // indirect github.com/sacloud/packages-go v0.0.10 // indirect
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect
github.com/sagernet/gvisor v0.0.0-20240214044702-a3d61928a32f // indirect github.com/sagernet/fswatch v0.1.1 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect
github.com/sagernet/quic-go v0.41.0-beta.2 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/sing-dns v0.2.0-beta.6 // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/quic-go v0.46.0-beta.4 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-dns v0.3.0-beta.14 // indirect
github.com/sagernet/sing-mux v0.2.0 // indirect github.com/sagernet/sing-mux v0.2.0 // indirect
github.com/sagernet/sing-quic v0.1.9-beta.1 // indirect github.com/sagernet/sing-quic v0.3.0-beta.3 // indirect
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
github.com/sagernet/sing-shadowtls v0.1.4 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect
github.com/sagernet/sing-tun v0.2.2-beta.3 // indirect github.com/sagernet/sing-tun v0.4.0-beta.13.0.20240703164908-1f043289199d // indirect
github.com/sagernet/sing-vmess v0.1.8 // indirect github.com/sagernet/sing-vmess v0.1.12 // indirect
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect
github.com/sagernet/utls v1.5.4 // indirect github.com/sagernet/utls v1.5.4 // indirect
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 // indirect
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect github.com/selectel/domains-go v1.1.0 // indirect
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect github.com/selectel/go-selvpcclient/v3 v3.1.1 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
github.com/softlayer/softlayer-go v1.1.2 // indirect github.com/softlayer/softlayer-go v1.1.5 // indirect
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/spf13/afero v1.9.3 // indirect github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.8.4 // indirect github.com/stretchr/testify v1.9.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect github.com/subosito/gotenv v1.4.2 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.898 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 // indirect
github.com/transip/gotransip/v6 v6.20.0 // indirect github.com/transip/gotransip/v6 v6.23.0 // indirect
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 // indirect github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 // indirect
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect github.com/vishvananda/netlink v1.3.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect github.com/vishvananda/netns v0.0.4 // indirect
github.com/vultr/govultr/v2 v2.17.2 // indirect github.com/vultr/govultr/v2 v2.17.2 // indirect
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect github.com/yandex-cloud/go-genproto v0.0.0-20240318083951-4fe6125f286e // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/blake3 v0.2.3 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/ratelimit v0.3.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.15.0 // indirect golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect golang.org/x/net v0.28.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.18.0 // indirect golang.org/x/tools v0.23.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
google.golang.org/api v0.162.0 // indirect google.golang.org/api v0.172.0 // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect google.golang.org/grpc v1.66.0 // indirect
google.golang.org/grpc v1.62.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect gopkg.in/ns1/ns1-go.v2 v2.7.13 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
lukechampine.com/blake3 v1.2.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect
) )
//github.com/apernet/hysteria/core v1.3.5-0.20240201034858-bb99579bb92c => /root/hysteria/core // replace github.com/sagernet/sing-box v1.10.0-alpha.22 => /root/sing-box_mod
replace github.com/sagernet/sing-box v1.9.0 => github.com/wyx2685/sing-box_mod v0.0.4 replace github.com/sagernet/sing-box v1.10.0-beta.5 => github.com/wyx2685/sing-box_mod v1.10.0-beta.5

587
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ package limiter
import ( import (
"errors" "errors"
"regexp" "regexp"
"strings"
"sync" "sync"
"time" "time"
@@ -20,13 +21,13 @@ var limiter map[string]*Limiter
func Init() { func Init() {
limiter = map[string]*Limiter{} limiter = map[string]*Limiter{}
c := task.Periodic{ c := task.Periodic{
Interval: time.Minute * 2, Interval: time.Minute * 3,
Execute: ClearOnlineIP, Execute: ClearOnlineIP,
} }
go func() { go func() {
log.WithField("Type", "Limiter"). log.WithField("Type", "Limiter").
Debug("ClearOnlineIP started") Debug("ClearOnlineIP started")
time.Sleep(time.Minute * 2) time.Sleep(time.Minute * 3)
_ = c.Start() _ = c.Start()
}() }()
} }
@@ -36,37 +37,46 @@ type Limiter struct {
ProtocolRules []string ProtocolRules []string
SpeedLimit int SpeedLimit int
UserOnlineIP *sync.Map // Key: Name, value: {Key: Ip, value: Uid} UserOnlineIP *sync.Map // Key: Name, value: {Key: Ip, value: Uid}
UUIDtoUID map[string]int // Key: UUID, value: UID OldUserOnline map[string]int // Key: Ip, value: Uid
UUIDtoUID map[string]int // Key: UUID, value: Uid
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
AliveList map[int]int // Key: Uid, value: alive_ip
} }
type UserLimitInfo struct { type UserLimitInfo struct {
UID int UID int
SpeedLimit int SpeedLimit int
DeviceLimit int
DynamicSpeedLimit int DynamicSpeedLimit int
ExpireTime int64 ExpireTime int64
OverLimit bool
} }
func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limiter { func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo, aliveList map[int]int) *Limiter {
info := &Limiter{ info := &Limiter{
SpeedLimit: l.SpeedLimit, SpeedLimit: l.SpeedLimit,
UserOnlineIP: new(sync.Map), UserOnlineIP: new(sync.Map),
UserLimitInfo: new(sync.Map), UserLimitInfo: new(sync.Map),
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime), ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
SpeedLimiter: new(sync.Map), SpeedLimiter: new(sync.Map),
AliveList: aliveList,
OldUserOnline: make(map[string]int),
} }
uuidmap := make(map[string]int) uuidmap := make(map[string]int)
for i := range users { for i := range users {
uuidmap[users[i].Uuid] = users[i].Id uuidmap[users[i].Uuid] = users[i].Id
userLimit := &UserLimitInfo{}
userLimit.UID = users[i].Id
if users[i].SpeedLimit != 0 { if users[i].SpeedLimit != 0 {
userLimit := &UserLimitInfo{ userLimit.SpeedLimit = users[i].SpeedLimit
UID: users[i].Id,
SpeedLimit: users[i].SpeedLimit,
}
info.UserLimitInfo.Store(format.UserTag(tag, users[i].Uuid), userLimit)
} }
if users[i].DeviceLimit != 0 {
userLimit.DeviceLimit = users[i].DeviceLimit
}
userLimit.OverLimit = false
info.UserLimitInfo.Store(format.UserTag(tag, users[i].Uuid), userLimit)
} }
info.UUIDtoUID = uuidmap info.UUIDtoUID = uuidmap
limitLock.Lock() limitLock.Lock()
@@ -97,14 +107,18 @@ func (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, deleted []panel
delete(l.UUIDtoUID, deleted[i].Uuid) delete(l.UUIDtoUID, deleted[i].Uuid)
} }
for i := range added { for i := range added {
if added[i].SpeedLimit != 0 { userLimit := &UserLimitInfo{
userLimit := &UserLimitInfo{ UID: added[i].Id,
UID: added[i].Id,
SpeedLimit: added[i].SpeedLimit,
ExpireTime: 0,
}
l.UserLimitInfo.Store(format.UserTag(tag, added[i].Uuid), userLimit)
} }
if added[i].SpeedLimit != 0 {
userLimit.SpeedLimit = added[i].SpeedLimit
userLimit.ExpireTime = 0
}
if added[i].DeviceLimit != 0 {
userLimit.DeviceLimit = added[i].DeviceLimit
}
userLimit.OverLimit = false
l.UserLimitInfo.Store(format.UserTag(tag, added[i].Uuid), userLimit)
l.UUIDtoUID[added[i].Uuid] = added[i].Id l.UUIDtoUID[added[i].Uuid] = added[i].Id
} }
} }
@@ -120,53 +134,71 @@ func (l *Limiter) UpdateDynamicSpeedLimit(tag, uuid string, limit int, expire ti
return nil return nil
} }
func (l *Limiter) CheckLimit(uuid string, ip string, isTcp bool) (Bucket *ratelimit.Bucket, Reject bool) { func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool) (Bucket *ratelimit.Bucket, Reject bool) {
// check if ipv4 mapped ipv6
ip = strings.TrimPrefix(ip, "::ffff:")
// ip and conn limiter // ip and conn limiter
if l.ConnLimiter.AddConnCount(uuid, ip, isTcp) { if l.ConnLimiter.AddConnCount(taguuid, ip, isTcp) {
return nil, true return nil, true
} }
// check and gen speed limit Bucket // check and gen speed limit Bucket
nodeLimit := l.SpeedLimit nodeLimit := l.SpeedLimit
userLimit := 0 userLimit := 0
if v, ok := l.UserLimitInfo.Load(uuid); ok { deviceLimit := 0
var uid int
if v, ok := l.UserLimitInfo.Load(taguuid); ok {
u := v.(*UserLimitInfo) u := v.(*UserLimitInfo)
deviceLimit = u.DeviceLimit
uid = u.UID
if u.ExpireTime < time.Now().Unix() && u.ExpireTime != 0 { if u.ExpireTime < time.Now().Unix() && u.ExpireTime != 0 {
if u.SpeedLimit != 0 { if u.SpeedLimit != 0 {
userLimit = u.SpeedLimit userLimit = u.SpeedLimit
u.DynamicSpeedLimit = 0 u.DynamicSpeedLimit = 0
u.ExpireTime = 0 u.ExpireTime = 0
} else { } else {
l.UserLimitInfo.Delete(uuid) l.UserLimitInfo.Delete(taguuid)
} }
} else { } else {
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit) userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
} }
} }
if noSSUDP {
// Store online user for device limit // Store online user for device limit
ipMap := new(sync.Map) ipMap := new(sync.Map)
uid := l.UUIDtoUID[uuid] ipMap.Store(ip, uid)
ipMap.Store(ip, uid) aliveIp := l.AliveList[uid]
// If any device is online // If any device is online
if v, ok := l.UserOnlineIP.LoadOrStore(uuid, ipMap); ok { if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
ipMap := v.(*sync.Map) ipMap := v.(*sync.Map)
// If this is a new ip // If this is a new ip
if _, ok := ipMap.LoadOrStore(ip, uid); !ok { if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
counter := 0 if deviceLimit > 0 {
ipMap.Range(func(key, value interface{}) bool { if deviceLimit <= aliveIp {
counter++ ipMap.Delete(ip)
return true return nil, true
}) }
}
}
} else if l.OldUserOnline[ip] == uid {
delete(l.OldUserOnline, ip)
} else {
if deviceLimit > 0 {
if deviceLimit <= aliveIp {
l.UserOnlineIP.Delete(taguuid)
return nil, true
}
}
} }
} }
limit := int64(determineSpeedLimit(nodeLimit, userLimit)) * 1000000 / 8 // If you need the Speed limit limit := int64(determineSpeedLimit(nodeLimit, userLimit)) * 1000000 / 8 // If you need the Speed limit
if limit > 0 { if limit > 0 {
Bucket = ratelimit.NewBucketWithQuantum(time.Second, limit, limit) // Byte/s Bucket = ratelimit.NewBucketWithQuantum(time.Second, limit, limit) // Byte/s
if v, ok := l.SpeedLimiter.LoadOrStore(uuid, Bucket); ok { if v, ok := l.SpeedLimiter.LoadOrStore(taguuid, Bucket); ok {
return v.(*ratelimit.Bucket), false return v.(*ratelimit.Bucket), false
} else { } else {
l.SpeedLimiter.Store(uuid, Bucket) l.SpeedLimiter.Store(taguuid, Bucket)
return Bucket, false return Bucket, false
} }
} else { } else {
@@ -176,17 +208,17 @@ func (l *Limiter) CheckLimit(uuid string, ip string, isTcp bool) (Bucket *rateli
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) { func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
var onlineUser []panel.OnlineUser var onlineUser []panel.OnlineUser
l.UserOnlineIP.Range(func(key, value interface{}) bool { l.UserOnlineIP.Range(func(key, value interface{}) bool {
email := key.(string) taguuid := key.(string)
ipMap := value.(*sync.Map) ipMap := value.(*sync.Map)
ipMap.Range(func(key, value interface{}) bool { ipMap.Range(func(key, value interface{}) bool {
uid := value.(int) uid := value.(int)
ip := key.(string) ip := key.(string)
l.OldUserOnline[ip] = uid
onlineUser = append(onlineUser, panel.OnlineUser{UID: uid, IP: ip}) onlineUser = append(onlineUser, panel.OnlineUser{UID: uid, IP: ip})
return true return true
}) })
l.UserOnlineIP.Delete(email) // Reset online device l.UserOnlineIP.Delete(taguuid) // Reset online device
return true return true
}) })
@@ -197,23 +229,3 @@ type UserIpList struct {
Uid int `json:"Uid"` Uid int `json:"Uid"`
IpList []string `json:"Ips"` IpList []string `json:"Ips"`
} }
func determineDeviceLimit(nodeLimit, userLimit int) (limit int) {
if nodeLimit == 0 || userLimit == 0 {
if nodeLimit > userLimit {
return nodeLimit
} else if nodeLimit < userLimit {
return userLimit
} else {
return 0
}
} else {
if nodeLimit > userLimit {
return userLimit
} else if nodeLimit < userLimit {
return nodeLimit
} else {
return nodeLimit
}
}
}

View File

@@ -1,16 +1,9 @@
package main package main
import ( import (
//"net/http"
//_ "net/http/pprof"
"github.com/InazumaV/V2bX/cmd" "github.com/InazumaV/V2bX/cmd"
) )
func main() { func main() {
//内存泄漏排查
//go func() {
// http.ListenAndServe("127.0.0.1:6060", nil)
//}()
cmd.Run() cmd.Run()
} }

View File

@@ -19,6 +19,7 @@ type Controller struct {
limiter *limiter.Limiter limiter *limiter.Limiter
traffic map[string]int64 traffic map[string]int64
userList []panel.UserInfo userList []panel.UserInfo
aliveMap map[int]int
info *panel.NodeInfo info *panel.NodeInfo
nodeInfoMonitorPeriodic *task.Task nodeInfoMonitorPeriodic *task.Task
userReportPeriodic *task.Task userReportPeriodic *task.Task
@@ -54,6 +55,10 @@ func (c *Controller) Start() error {
if len(c.userList) == 0 { if len(c.userList) == 0 {
return errors.New("add users error: not have any user") return errors.New("add users error: not have any user")
} }
c.aliveMap, err = c.apiClient.GetUserAlive()
if err != nil {
return fmt.Errorf("failed to get user alive list: %s", err)
}
if len(c.Options.Name) == 0 { if len(c.Options.Name) == 0 {
c.tag = c.buildNodeTag(node) c.tag = c.buildNodeTag(node)
} else { } else {
@@ -61,7 +66,7 @@ func (c *Controller) Start() error {
} }
// add limiter // add limiter
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList) l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, c.aliveMap)
// add rule limiter // add rule limiter
if err = l.UpdateRule(&node.Rules); err != nil { if err = l.UpdateRule(&node.Rules); err != nil {
return fmt.Errorf("update rule error: %s", err) return fmt.Errorf("update rule error: %s", err)

View File

@@ -68,6 +68,11 @@ func (c *Controller) nodeInfoMonitor() (err error) {
}).Error("Get user list failed") }).Error("Get user list failed")
return nil return nil
} }
// get user alive
newA, err := c.apiClient.GetUserAlive()
if err != nil {
return err
}
if newN != nil { if newN != nil {
c.info = newN c.info = newN
// nodeInfo changed // nodeInfo changed
@@ -92,7 +97,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
// Remove Old limiter // Remove Old limiter
limiter.DeleteLimiter(c.tag) limiter.DeleteLimiter(c.tag)
// Add new Limiter // Add new Limiter
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList) l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, newA)
c.limiter = l c.limiter = l
} }
// Update rule // Update rule
@@ -154,7 +159,10 @@ func (c *Controller) nodeInfoMonitor() (err error) {
// exit // exit
return nil return nil
} }
// update alive list
if newA != nil {
c.limiter.AliveList = newA
}
// node no changed, check users // node no changed, check users
if len(newU) == 0 { if len(newU) == 0 {
return nil return nil

View File

@@ -1,7 +1,6 @@
package node package node
import ( import (
"fmt"
"strconv" "strconv"
"github.com/InazumaV/V2bX/api/panel" "github.com/InazumaV/V2bX/api/panel"
@@ -15,11 +14,7 @@ func (c *Controller) reportUserTrafficTask() (err error) {
up, down := c.server.GetUserTraffic(c.tag, c.userList[i].Uuid, true) up, down := c.server.GetUserTraffic(c.tag, c.userList[i].Uuid, true)
if up > 0 || down > 0 { if up > 0 || down > 0 {
if c.LimitConfig.EnableDynamicSpeedLimit { if c.LimitConfig.EnableDynamicSpeedLimit {
if _, ok := c.traffic[c.userList[i].Uuid]; ok { c.traffic[c.userList[i].Uuid] += up + down
c.traffic[c.userList[i].Uuid] += up + down
} else {
c.traffic[c.userList[i].Uuid] = up + down
}
} }
userTraffic = append(userTraffic, panel.UserTraffic{ userTraffic = append(userTraffic, panel.UserTraffic{
UID: (c.userList)[i].Id, UID: (c.userList)[i].Id,
@@ -56,18 +51,12 @@ func (c *Controller) reportUserTrafficTask() (err error) {
result = append(result, online) result = append(result, online)
} }
} }
reportOnline := make(map[int]int)
data := make(map[int][]string) data := make(map[int][]string)
for _, onlineuser := range result { for _, onlineuser := range result {
// json structure: { UID1:["ip1","ip2"],UID2:["ip3","ip4"] } // json structure: { UID1:["ip1","ip2"],UID2:["ip3","ip4"] }
data[onlineuser.UID] = append(data[onlineuser.UID], fmt.Sprintf("%s_%d", onlineuser.IP, c.info.Id)) data[onlineuser.UID] = append(data[onlineuser.UID], onlineuser.IP)
if _, ok := reportOnline[onlineuser.UID]; ok {
reportOnline[onlineuser.UID]++
} else {
reportOnline[onlineuser.UID] = 1
}
} }
if err = c.apiClient.ReportNodeOnlineUsers(&data, &reportOnline); err != nil { if err = c.apiClient.ReportNodeOnlineUsers(&data); err != nil {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"tag": c.tag, "tag": c.tag,
"err": err, "err": err,