mirror of
https://github.com/wyx2685/V2bX.git
synced 2026-02-04 20:50:09 +00:00
Compare commits
19 Commits
v0.0.0-202
...
v0.0.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33d9ab4b0a | ||
|
|
a7637d436f | ||
|
|
4dda65a636 | ||
|
|
6c725b424f | ||
|
|
a052a1f1e8 | ||
|
|
85ad40d098 | ||
|
|
206af0216c | ||
|
|
ed5edda28a | ||
|
|
995e606694 | ||
|
|
96b457d679 | ||
|
|
55b20f5550 | ||
|
|
c0b31837e4 | ||
|
|
bf4a52df4d | ||
|
|
423ac622b5 | ||
|
|
625265148f | ||
|
|
73f9b19222 | ||
|
|
0d274bcf61 | ||
|
|
77ec03016c | ||
|
|
1d4945af8d |
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -109,7 +109,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21.4'
|
go-version: '1.22.0'
|
||||||
|
|
||||||
- name: Get project dependencies
|
- name: Get project dependencies
|
||||||
run: go mod download
|
run: go mod download
|
||||||
@@ -125,13 +125,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "version: $version"
|
echo "version: $version"
|
||||||
mkdir -p build_assets
|
mkdir -p build_assets
|
||||||
go build -v -o build_assets/V2bX -tags "sing xray with_reality_server with_quic with_grpc with_utls with_wireguard with_acme" -trimpath -ldflags "-X 'github.com/InazumaV/V2bX/cmd.version=$version' -s -w -buildid="
|
go build -v -o build_assets/V2bX -tags "sing xray hysteria2 with_reality_server with_quic with_grpc with_utls with_wireguard with_acme" -trimpath -ldflags "-X 'github.com/InazumaV/V2bX/cmd.version=$version' -s -w -buildid="
|
||||||
|
|
||||||
- name: Build Mips softfloat V2bX
|
- name: Build Mips softfloat V2bX
|
||||||
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||||
run: |
|
run: |
|
||||||
echo "version: $version"
|
echo "version: $version"
|
||||||
GOMIPS=softfloat go build -v -o build_assets/V2bX_softfloat -tags "sing xray with_reality_server with_quic with_grpc with_utls with_wireguard with_acme" -trimpath -ldflags "-X 'github.com/InazumaV/V2bX/cmd.version=$version' -s -w -buildid="
|
GOMIPS=softfloat go build -v -o build_assets/V2bX_softfloat -tags "sing xray hysteria2 with_reality_server with_quic with_grpc with_utls with_wireguard with_acme" -trimpath -ldflags "-X 'github.com/InazumaV/V2bX/cmd.version=$version' -s -w -buildid="
|
||||||
- name: Rename Windows V2bX
|
- name: Rename Windows V2bX
|
||||||
if: matrix.goos == 'windows'
|
if: matrix.goos == 'windows'
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,4 +12,6 @@ app/legocmd/.lego/
|
|||||||
example/.lego
|
example/.lego
|
||||||
example/cert
|
example/cert
|
||||||
./vscode
|
./vscode
|
||||||
.idea/*
|
output/*
|
||||||
|
.idea/*
|
||||||
|
newV2bX.sh
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Build go
|
# Build go
|
||||||
FROM golang:1.21.4-alpine AS builder
|
FROM golang:1.22.0-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
RUN go build -v -o V2bX -tags "sing xray with_reality_server with_quic with_grpc with_utls with_wireguard with_acme"
|
RUN go build -v -o V2bX -tags "sing xray hysteria2 with_reality_server with_quic with_grpc with_utls with_wireguard with_acme"
|
||||||
|
|
||||||
# Release
|
# Release
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|||||||
@@ -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 增长记录
|
||||||
|
|
||||||
[](https://starchart.cc/InazumaV/V2bX)
|
[](https://starchart.cc/wyx2685/V2bX)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package panel
|
package panel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -78,7 +80,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 {
|
||||||
@@ -130,7 +132,19 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
|
|||||||
r, err := c.client.
|
r, err := c.client.
|
||||||
R().
|
R().
|
||||||
SetHeader("If-None-Match", c.nodeEtag).
|
SetHeader("If-None-Match", c.nodeEtag).
|
||||||
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
|
|
||||||
|
if r.StatusCode() == 304 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
hash := sha256.Sum256(r.Body())
|
||||||
|
newBodyHash := hex.EncodeToString(hash[:])
|
||||||
|
if c.responseBodyHash == newBodyHash {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
c.responseBodyHash = newBodyHash
|
||||||
|
c.nodeEtag = r.Header().Get("ETag")
|
||||||
if err = c.checkResponse(r, path, err); err != nil {
|
if err = c.checkResponse(r, path, err); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -141,9 +155,6 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
|
|||||||
r.RawBody().Close()
|
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")
|
||||||
}
|
}
|
||||||
@@ -274,8 +285,7 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
|
|||||||
cm.Routes = nil
|
cm.Routes = nil
|
||||||
cm.BaseConfig = nil
|
cm.BaseConfig = nil
|
||||||
|
|
||||||
c.nodeEtag = r.Header().Get("ETag")
|
return node, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func intervalToTime(i interface{}) time.Duration {
|
func intervalToTime(i interface{}) time.Duration {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Client struct {
|
|||||||
NodeId int
|
NodeId int
|
||||||
nodeEtag string
|
nodeEtag string
|
||||||
userEtag string
|
userEtag string
|
||||||
|
responseBodyHash string
|
||||||
LastReportOnline map[int]int
|
LastReportOnline map[int]int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func (c *Client) GetUserList() (UserList []UserInfo, err 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).
|
||||||
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
if err = c.checkResponse(r, path, err); err != nil {
|
if err = c.checkResponse(r, path, err); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -47,14 +48,16 @@ func (c *Client) GetUserList() (UserList []UserInfo, err error) {
|
|||||||
return nil, fmt.Errorf("received nil response")
|
return nil, fmt.Errorf("received nil response")
|
||||||
}
|
}
|
||||||
var userList *UserListBody
|
var userList *UserListBody
|
||||||
err = json.Unmarshal(r.Body(), &userList)
|
|
||||||
if err != nil {
|
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)
|
return nil, fmt.Errorf("unmarshal userlist error: %s", err)
|
||||||
}
|
}
|
||||||
c.userEtag = r.Header().Get("ETag")
|
c.userEtag = r.Header().Get("ETag")
|
||||||
|
|
||||||
var userinfos []UserInfo
|
var userinfos []UserInfo
|
||||||
var localDeviceLimit int = 0
|
var deviceLimit, localDeviceLimit int = 0, 0
|
||||||
for _, user := range userList.Users {
|
for _, user := range userList.Users {
|
||||||
// If there is still device available, add the user
|
// If there is still device available, add the user
|
||||||
if user.DeviceLimit > 0 && user.AliveIp > 0 {
|
if user.DeviceLimit > 0 && user.AliveIp > 0 {
|
||||||
@@ -65,13 +68,14 @@ func (c *Client) GetUserList() (UserList []UserInfo, err error) {
|
|||||||
// If there are any available device.
|
// If there are any available device.
|
||||||
localDeviceLimit = user.DeviceLimit - user.AliveIp + lastOnline
|
localDeviceLimit = user.DeviceLimit - user.AliveIp + lastOnline
|
||||||
if localDeviceLimit > 0 {
|
if localDeviceLimit > 0 {
|
||||||
|
deviceLimit = localDeviceLimit
|
||||||
} else if lastOnline > 0 {
|
} else if lastOnline > 0 {
|
||||||
|
deviceLimit = lastOnline
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
user.DeviceLimit = deviceLimit
|
||||||
userinfos = append(userinfos, user)
|
userinfos = append(userinfos, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
conf/core.go
12
conf/core.go
@@ -5,10 +5,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CoreConfig struct {
|
type CoreConfig struct {
|
||||||
Type string `json:"Type"`
|
Type string `json:"Type"`
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
XrayConfig *XrayConfig `json:"-"`
|
XrayConfig *XrayConfig `json:"-"`
|
||||||
SingConfig *SingConfig `json:"-"`
|
SingConfig *SingConfig `json:"-"`
|
||||||
|
Hysteria2Config *Hysteria2Config `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type _CoreConfig CoreConfig
|
type _CoreConfig CoreConfig
|
||||||
@@ -25,6 +26,9 @@ func (c *CoreConfig) UnmarshalJSON(b []byte) error {
|
|||||||
case "sing":
|
case "sing":
|
||||||
c.SingConfig = NewSingConfig()
|
c.SingConfig = NewSingConfig()
|
||||||
return json.Unmarshal(b, c.SingConfig)
|
return json.Unmarshal(b, c.SingConfig)
|
||||||
|
case "hysteria2":
|
||||||
|
c.Hysteria2Config = NewHysteria2Config()
|
||||||
|
return json.Unmarshal(b, c.Hysteria2Config)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
17
conf/hy.go
Normal file
17
conf/hy.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
type Hysteria2Config struct {
|
||||||
|
LogConfig Hysteria2LogConfig `json:"Log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hysteria2LogConfig struct {
|
||||||
|
Level string `json:"Level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHysteria2Config() *Hysteria2Config {
|
||||||
|
return &Hysteria2Config{
|
||||||
|
LogConfig: Hysteria2LogConfig{
|
||||||
|
Level: "error",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,6 +113,7 @@ type Options struct {
|
|||||||
RawOptions json.RawMessage `json:"RawOptions"`
|
RawOptions json.RawMessage `json:"RawOptions"`
|
||||||
XrayOptions *XrayOptions `json:"XrayOptions"`
|
XrayOptions *XrayOptions `json:"XrayOptions"`
|
||||||
SingOptions *SingOptions `json:"SingOptions"`
|
SingOptions *SingOptions `json:"SingOptions"`
|
||||||
|
Hysteria2ConfigPath string `json:"Hysteria2ConfigPath"`
|
||||||
CertConfig *CertConfig `json:"CertConfig"`
|
CertConfig *CertConfig `json:"CertConfig"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +130,9 @@ func (o *Options) UnmarshalJSON(data []byte) error {
|
|||||||
case "sing":
|
case "sing":
|
||||||
o.SingOptions = NewSingOptions()
|
o.SingOptions = NewSingOptions()
|
||||||
return json.Unmarshal(data, o.SingOptions)
|
return json.Unmarshal(data, o.SingOptions)
|
||||||
|
case "hysteria2":
|
||||||
|
o.RawOptions = data
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
o.Core = ""
|
o.Core = ""
|
||||||
o.RawOptions = data
|
o.RawOptions = data
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
423
core/hy2/config.go
Normal file
423
core/hy2/config.go
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
|
"github.com/InazumaV/V2bX/conf"
|
||||||
|
"github.com/apernet/hysteria/core/v2/server"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/correctnet"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/masq"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/obfs"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/outbounds"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type masqHandlerLogWrapper struct {
|
||||||
|
H http.Handler
|
||||||
|
QUIC bool
|
||||||
|
Logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *masqHandlerLogWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
m.Logger.Debug("masquerade request",
|
||||||
|
zap.String("addr", r.RemoteAddr),
|
||||||
|
zap.String("method", r.Method),
|
||||||
|
zap.String("host", r.Host),
|
||||||
|
zap.String("url", r.URL.String()),
|
||||||
|
zap.Bool("quic", m.QUIC))
|
||||||
|
m.H.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Byte = 1
|
||||||
|
Kilobyte = Byte * 1000
|
||||||
|
Megabyte = Kilobyte * 1000
|
||||||
|
Gigabyte = Megabyte * 1000
|
||||||
|
Terabyte = Gigabyte * 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultStreamReceiveWindow = 8388608 // 8MB
|
||||||
|
defaultConnReceiveWindow = defaultStreamReceiveWindow * 5 / 2 // 20MB
|
||||||
|
defaultMaxIdleTimeout = 30 * time.Second
|
||||||
|
defaultMaxIncomingStreams = 4096
|
||||||
|
defaultUDPIdleTimeout = 60 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
func (n *Hysteria2node) getTLSConfig(config *conf.Options) (*server.TLSConfig, error) {
|
||||||
|
if config.CertConfig == nil {
|
||||||
|
return nil, fmt.Errorf("the CertConfig is not vail")
|
||||||
|
}
|
||||||
|
switch config.CertConfig.CertMode {
|
||||||
|
case "none", "":
|
||||||
|
return nil, fmt.Errorf("the CertMode cannot be none")
|
||||||
|
default:
|
||||||
|
var certs []tls.Certificate
|
||||||
|
cert, err := tls.LoadX509KeyPair(config.CertConfig.CertFile, config.CertConfig.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certs = append(certs, cert)
|
||||||
|
return &server.TLSConfig{
|
||||||
|
Certificates: certs,
|
||||||
|
GetCertificate: func(tlsinfo *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair(config.CertConfig.CertFile, config.CertConfig.KeyFile)
|
||||||
|
return &cert, err
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Hysteria2node) getQUICConfig(config *serverConfig) (*server.QUICConfig, error) {
|
||||||
|
quic := &server.QUICConfig{}
|
||||||
|
if config.QUIC.InitStreamReceiveWindow == 0 {
|
||||||
|
quic.InitialStreamReceiveWindow = defaultStreamReceiveWindow
|
||||||
|
} else if config.QUIC.InitStreamReceiveWindow < 16384 {
|
||||||
|
return nil, fmt.Errorf("QUICConfig.InitialStreamReceiveWindowf must be at least 16384")
|
||||||
|
} else {
|
||||||
|
quic.InitialConnectionReceiveWindow = config.QUIC.InitConnectionReceiveWindow
|
||||||
|
}
|
||||||
|
if config.QUIC.MaxStreamReceiveWindow == 0 {
|
||||||
|
quic.MaxStreamReceiveWindow = defaultStreamReceiveWindow
|
||||||
|
} else if config.QUIC.MaxStreamReceiveWindow < 16384 {
|
||||||
|
return nil, fmt.Errorf("QUICConfig.MaxStreamReceiveWindowf must be at least 16384")
|
||||||
|
} else {
|
||||||
|
quic.MaxStreamReceiveWindow = config.QUIC.MaxStreamReceiveWindow
|
||||||
|
}
|
||||||
|
if config.QUIC.InitConnectionReceiveWindow == 0 {
|
||||||
|
quic.InitialConnectionReceiveWindow = defaultConnReceiveWindow
|
||||||
|
} else if config.QUIC.InitConnectionReceiveWindow < 16384 {
|
||||||
|
return nil, fmt.Errorf("QUICConfig.InitialConnectionReceiveWindowf must be at least 16384")
|
||||||
|
} else {
|
||||||
|
quic.InitialConnectionReceiveWindow = config.QUIC.InitConnectionReceiveWindow
|
||||||
|
}
|
||||||
|
if config.QUIC.MaxConnectionReceiveWindow == 0 {
|
||||||
|
quic.MaxConnectionReceiveWindow = defaultConnReceiveWindow
|
||||||
|
} else if config.QUIC.MaxConnectionReceiveWindow < 16384 {
|
||||||
|
return nil, fmt.Errorf("QUICConfig.MaxConnectionReceiveWindowf must be at least 16384")
|
||||||
|
} else {
|
||||||
|
quic.MaxConnectionReceiveWindow = config.QUIC.MaxConnectionReceiveWindow
|
||||||
|
}
|
||||||
|
if config.QUIC.MaxIdleTimeout == 0 {
|
||||||
|
quic.MaxIdleTimeout = defaultMaxIdleTimeout
|
||||||
|
} else if config.QUIC.MaxIdleTimeout < 4*time.Second || config.QUIC.MaxIdleTimeout > 120*time.Second {
|
||||||
|
return nil, fmt.Errorf("QUICConfig.MaxIdleTimeoutf must be between 4s and 120s")
|
||||||
|
} else {
|
||||||
|
quic.MaxIdleTimeout = config.QUIC.MaxIdleTimeout
|
||||||
|
}
|
||||||
|
if config.QUIC.MaxIncomingStreams == 0 {
|
||||||
|
quic.MaxIncomingStreams = defaultMaxIncomingStreams
|
||||||
|
} else if config.QUIC.MaxIncomingStreams < 8 {
|
||||||
|
return nil, fmt.Errorf("QUICConfig.MaxIncomingStreamsf must be at least 8")
|
||||||
|
} else {
|
||||||
|
quic.MaxIncomingStreams = config.QUIC.MaxIncomingStreams
|
||||||
|
}
|
||||||
|
// todo fix !linux && !windows && !darwin
|
||||||
|
quic.DisablePathMTUDiscovery = false
|
||||||
|
|
||||||
|
return quic, nil
|
||||||
|
}
|
||||||
|
func (n *Hysteria2node) getConn(info *panel.NodeInfo, config *conf.Options) (net.PacketConn, error) {
|
||||||
|
uAddr, err := net.ResolveUDPAddr("udp", formatAddress(config.ListenIP, info.Common.ServerPort))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn, err := correctnet.ListenUDP("udp", uAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch strings.ToLower(info.Hysteria2.ObfsType) {
|
||||||
|
case "", "plain":
|
||||||
|
return conn, nil
|
||||||
|
case "salamander":
|
||||||
|
ob, err := obfs.NewSalamanderObfuscator([]byte(info.Hysteria2.ObfsPassword))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obfs.WrapPacketConn(conn, ob), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported obfuscation type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Hysteria2node) getBandwidthConfig(info *panel.NodeInfo) *server.BandwidthConfig {
|
||||||
|
band := &server.BandwidthConfig{}
|
||||||
|
if info.Hysteria2.UpMbps != 0 {
|
||||||
|
band.MaxTx = (uint64)(info.Hysteria2.UpMbps * Megabyte / 8)
|
||||||
|
}
|
||||||
|
if info.Hysteria2.DownMbps != 0 {
|
||||||
|
band.MaxRx = (uint64)(info.Hysteria2.DownMbps * Megabyte / 8)
|
||||||
|
|
||||||
|
}
|
||||||
|
return band
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Hysteria2node) getOutboundConfig(c *serverConfig) (server.Outbound, error) {
|
||||||
|
// Resolver, ACL, actual outbound are all implemented through the Outbound interface.
|
||||||
|
// Depending on the config, we build a chain like this:
|
||||||
|
// Resolver(ACL(Outbounds...))
|
||||||
|
|
||||||
|
// Outbounds
|
||||||
|
var obs []outbounds.OutboundEntry
|
||||||
|
if len(c.Outbounds) == 0 {
|
||||||
|
// Guarantee we have at least one outbound
|
||||||
|
obs = []outbounds.OutboundEntry{{
|
||||||
|
Name: "default",
|
||||||
|
Outbound: outbounds.NewDirectOutboundSimple(outbounds.DirectOutboundModeAuto),
|
||||||
|
}}
|
||||||
|
} else {
|
||||||
|
obs = make([]outbounds.OutboundEntry, len(c.Outbounds))
|
||||||
|
for i, entry := range c.Outbounds {
|
||||||
|
if entry.Name == "" {
|
||||||
|
return nil, fmt.Errorf("empty outbound name")
|
||||||
|
}
|
||||||
|
var ob outbounds.PluggableOutbound
|
||||||
|
var err error
|
||||||
|
switch strings.ToLower(entry.Type) {
|
||||||
|
case "direct":
|
||||||
|
ob, err = serverConfigOutboundDirectToOutbound(entry.Direct)
|
||||||
|
case "socks5":
|
||||||
|
ob, err = serverConfigOutboundSOCKS5ToOutbound(entry.SOCKS5)
|
||||||
|
case "http":
|
||||||
|
ob, err = serverConfigOutboundHTTPToOutbound(entry.HTTP)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("outbounds.type unsupported outbound type")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
obs[i] = outbounds.OutboundEntry{Name: entry.Name, Outbound: ob}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var uOb outbounds.PluggableOutbound // "unified" outbound
|
||||||
|
|
||||||
|
// ACL
|
||||||
|
hasACL := false
|
||||||
|
if c.ACL.File != "" && len(c.ACL.Inline) > 0 {
|
||||||
|
return nil, fmt.Errorf("cannot set both acl.file and acl.inline")
|
||||||
|
}
|
||||||
|
gLoader := &GeoLoader{
|
||||||
|
GeoIPFilename: c.ACL.GeoIP,
|
||||||
|
GeoSiteFilename: c.ACL.GeoSite,
|
||||||
|
UpdateInterval: c.ACL.GeoUpdateInterval,
|
||||||
|
Logger: n.Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ACL.File != "" {
|
||||||
|
hasACL = true
|
||||||
|
acl, err := outbounds.NewACLEngineFromFile(c.ACL.File, obs, gLoader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
uOb = acl
|
||||||
|
} else if len(c.ACL.Inline) > 0 {
|
||||||
|
n.Logger.Debug("found ACL Inline:", zap.Strings("Inline", c.ACL.Inline))
|
||||||
|
hasACL = true
|
||||||
|
acl, err := outbounds.NewACLEngineFromString(strings.Join(c.ACL.Inline, "\n"), obs, gLoader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
uOb = acl
|
||||||
|
} else {
|
||||||
|
// No ACL, use the first outbound
|
||||||
|
uOb = obs[0].Outbound
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(c.Resolver.Type) {
|
||||||
|
case "", "system":
|
||||||
|
if hasACL {
|
||||||
|
// If the user uses ACL, we must put a resolver in front of it,
|
||||||
|
// for IP rules to work on domain requests.
|
||||||
|
uOb = outbounds.NewSystemResolver(uOb)
|
||||||
|
}
|
||||||
|
// Otherwise we can just rely on outbound handling on its own.
|
||||||
|
case "tcp":
|
||||||
|
if c.Resolver.TCP.Addr == "" {
|
||||||
|
return nil, fmt.Errorf("empty resolver address")
|
||||||
|
}
|
||||||
|
uOb = outbounds.NewStandardResolverTCP(c.Resolver.TCP.Addr, c.Resolver.TCP.Timeout, uOb)
|
||||||
|
case "udp":
|
||||||
|
if c.Resolver.UDP.Addr == "" {
|
||||||
|
return nil, fmt.Errorf("empty resolver address")
|
||||||
|
}
|
||||||
|
uOb = outbounds.NewStandardResolverUDP(c.Resolver.UDP.Addr, c.Resolver.UDP.Timeout, uOb)
|
||||||
|
case "tls", "tcp-tls":
|
||||||
|
if c.Resolver.TLS.Addr == "" {
|
||||||
|
return nil, fmt.Errorf("empty resolver address")
|
||||||
|
}
|
||||||
|
uOb = outbounds.NewStandardResolverTLS(c.Resolver.TLS.Addr, c.Resolver.TLS.Timeout, c.Resolver.TLS.SNI, c.Resolver.TLS.Insecure, uOb)
|
||||||
|
case "https", "http":
|
||||||
|
if c.Resolver.HTTPS.Addr == "" {
|
||||||
|
return nil, fmt.Errorf("empty resolver address")
|
||||||
|
}
|
||||||
|
uOb = outbounds.NewDoHResolver(c.Resolver.HTTPS.Addr, c.Resolver.HTTPS.Timeout, c.Resolver.HTTPS.SNI, c.Resolver.HTTPS.Insecure, uOb)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resolver type")
|
||||||
|
}
|
||||||
|
Outbound := &outbounds.PluggableOutboundAdapter{PluggableOutbound: uOb}
|
||||||
|
|
||||||
|
return Outbound, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Hysteria2node) getMasqHandler(tlsconfig *server.TLSConfig, conn net.PacketConn, c *serverConfig) (http.Handler, error) {
|
||||||
|
var handler http.Handler
|
||||||
|
switch strings.ToLower(c.Masquerade.Type) {
|
||||||
|
case "", "404":
|
||||||
|
handler = http.NotFoundHandler()
|
||||||
|
case "file":
|
||||||
|
if c.Masquerade.File.Dir == "" {
|
||||||
|
return nil, fmt.Errorf("masquerade.file.dir empty file directory")
|
||||||
|
}
|
||||||
|
handler = http.FileServer(http.Dir(c.Masquerade.File.Dir))
|
||||||
|
case "proxy":
|
||||||
|
if c.Masquerade.Proxy.URL == "" {
|
||||||
|
return nil, fmt.Errorf("masquerade.proxy.url empty proxy url")
|
||||||
|
}
|
||||||
|
u, err := url.Parse(c.Masquerade.Proxy.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(fmt.Sprintf("masquerade.proxy.url %s", err))
|
||||||
|
}
|
||||||
|
handler = &httputil.ReverseProxy{
|
||||||
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
|
r.SetURL(u)
|
||||||
|
// SetURL rewrites the Host header,
|
||||||
|
// but we don't want that if rewriteHost is false
|
||||||
|
if !c.Masquerade.Proxy.RewriteHost {
|
||||||
|
r.Out.Host = r.In.Host
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
n.Logger.Error("HTTP reverse proxy error", zap.Error(err))
|
||||||
|
w.WriteHeader(http.StatusBadGateway)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "string":
|
||||||
|
if c.Masquerade.String.Content == "" {
|
||||||
|
return nil, fmt.Errorf("masquerade.string.content empty string content")
|
||||||
|
}
|
||||||
|
if c.Masquerade.String.StatusCode != 0 &&
|
||||||
|
(c.Masquerade.String.StatusCode < 200 ||
|
||||||
|
c.Masquerade.String.StatusCode > 599 ||
|
||||||
|
c.Masquerade.String.StatusCode == 233) {
|
||||||
|
// 233 is reserved for Hysteria authentication
|
||||||
|
return nil, fmt.Errorf("masquerade.string.statusCode invalid status code (must be 200-599, except 233)")
|
||||||
|
}
|
||||||
|
handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
for k, v := range c.Masquerade.String.Headers {
|
||||||
|
w.Header().Set(k, v)
|
||||||
|
}
|
||||||
|
if c.Masquerade.String.StatusCode != 0 {
|
||||||
|
w.WriteHeader(c.Masquerade.String.StatusCode)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusOK) // Use 200 OK by default
|
||||||
|
}
|
||||||
|
_, _ = w.Write([]byte(c.Masquerade.String.Content))
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("masquerade.type unsupported masquerade type")
|
||||||
|
}
|
||||||
|
MasqHandler := &masqHandlerLogWrapper{H: handler, QUIC: true, Logger: n.Logger}
|
||||||
|
|
||||||
|
if c.Masquerade.ListenHTTP != "" || c.Masquerade.ListenHTTPS != "" {
|
||||||
|
if c.Masquerade.ListenHTTP != "" && c.Masquerade.ListenHTTPS == "" {
|
||||||
|
return nil, fmt.Errorf("masquerade.listenHTTPS having only HTTP server without HTTPS is not supported")
|
||||||
|
}
|
||||||
|
s := masq.MasqTCPServer{
|
||||||
|
QUICPort: extractPortFromAddr(conn.LocalAddr().String()),
|
||||||
|
HTTPSPort: extractPortFromAddr(c.Masquerade.ListenHTTPS),
|
||||||
|
Handler: &masqHandlerLogWrapper{H: handler, QUIC: false},
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
Certificates: tlsconfig.Certificates,
|
||||||
|
GetCertificate: tlsconfig.GetCertificate,
|
||||||
|
},
|
||||||
|
ForceHTTPS: c.Masquerade.ForceHTTPS,
|
||||||
|
}
|
||||||
|
go runMasqTCPServer(&s, c.Masquerade.ListenHTTP, c.Masquerade.ListenHTTPS, n.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MasqHandler, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Hysteria2node) getHyConfig(info *panel.NodeInfo, config *conf.Options, c *serverConfig) (*server.Config, error) {
|
||||||
|
tls, err := n.getTLSConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quic, err := n.getQUICConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn, err := n.getConn(info, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
Outbound, err := n.getOutboundConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
Masq, err := n.getMasqHandler(tls, conn, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &server.Config{
|
||||||
|
TLSConfig: *tls,
|
||||||
|
QUICConfig: *quic,
|
||||||
|
Conn: conn,
|
||||||
|
Outbound: Outbound,
|
||||||
|
BandwidthConfig: *n.getBandwidthConfig(info),
|
||||||
|
IgnoreClientBandwidth: c.IgnoreClientBandwidth,
|
||||||
|
DisableUDP: c.DisableUDP,
|
||||||
|
UDPIdleTimeout: c.UDPIdleTimeout,
|
||||||
|
EventLogger: n.EventLogger,
|
||||||
|
TrafficLogger: n.TrafficLogger,
|
||||||
|
MasqHandler: Masq,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMasqTCPServer(s *masq.MasqTCPServer, httpAddr, httpsAddr string, logger *zap.Logger) {
|
||||||
|
errChan := make(chan error, 2)
|
||||||
|
if httpAddr != "" {
|
||||||
|
go func() {
|
||||||
|
logger.Info("masquerade HTTP server up and running", zap.String("listen", httpAddr))
|
||||||
|
errChan <- s.ListenAndServeHTTP(httpAddr)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if httpsAddr != "" {
|
||||||
|
go func() {
|
||||||
|
logger.Info("masquerade HTTPS server up and running", zap.String("listen", httpsAddr))
|
||||||
|
errChan <- s.ListenAndServeHTTPS(httpsAddr)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
err := <-errChan
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to serve masquerade HTTP(S)", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractPortFromAddr(addr string) int {
|
||||||
|
_, portStr, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
port, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatAddress(ip string, port int) string {
|
||||||
|
if strings.Contains(ip, ":") {
|
||||||
|
return fmt.Sprintf("[%s]:%d", ip, port)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%d", ip, port)
|
||||||
|
}
|
||||||
181
core/hy2/geoloader.go
Normal file
181
core/hy2/geoloader.go
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/extras/v2/outbounds/acl"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
geoipFilename = "geoip.dat"
|
||||||
|
geoipURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat"
|
||||||
|
geositeFilename = "geosite.dat"
|
||||||
|
geositeURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
|
||||||
|
geoDlTmpPattern = ".hysteria-geoloader.dlpart.*"
|
||||||
|
|
||||||
|
geoDefaultUpdateInterval = 7 * 24 * time.Hour // 7 days
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ acl.GeoLoader = (*GeoLoader)(nil)
|
||||||
|
|
||||||
|
// GeoLoader provides the on-demand GeoIP/GeoSite database
|
||||||
|
// loading functionality required by the ACL engine.
|
||||||
|
// Empty filenames = automatic download from built-in URLs.
|
||||||
|
type GeoLoader struct {
|
||||||
|
GeoIPFilename string
|
||||||
|
GeoSiteFilename string
|
||||||
|
UpdateInterval time.Duration
|
||||||
|
|
||||||
|
geoipMap map[string]*v2geo.GeoIP
|
||||||
|
geositeMap map[string]*v2geo.GeoSite
|
||||||
|
|
||||||
|
Logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GeoLoader) shouldDownload(filename string) bool {
|
||||||
|
info, err := os.Stat(filename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if info.Size() == 0 {
|
||||||
|
// empty files are loadable by v2geo, but we consider it broken
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
dt := time.Since(info.ModTime())
|
||||||
|
if l.UpdateInterval == 0 {
|
||||||
|
return dt > geoDefaultUpdateInterval
|
||||||
|
} else {
|
||||||
|
return dt > l.UpdateInterval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GeoLoader) downloadAndCheck(filename, url string, checkFunc func(filename string) error) error {
|
||||||
|
l.geoDownloadFunc(filename, url)
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
l.geoDownloadErrFunc(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
f, err := os.CreateTemp(".", geoDlTmpPattern)
|
||||||
|
if err != nil {
|
||||||
|
l.geoDownloadErrFunc(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
_, err = io.Copy(f, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
l.geoDownloadErrFunc(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
err = checkFunc(f.Name())
|
||||||
|
if err != nil {
|
||||||
|
l.geoDownloadErrFunc(fmt.Errorf("integrity check failed: %w", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Rename(f.Name(), filename)
|
||||||
|
if err != nil {
|
||||||
|
l.geoDownloadErrFunc(fmt.Errorf("rename failed: %w", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) {
|
||||||
|
if l.geoipMap != nil {
|
||||||
|
return l.geoipMap, nil
|
||||||
|
}
|
||||||
|
autoDL := false
|
||||||
|
filename := l.GeoIPFilename
|
||||||
|
if filename == "" {
|
||||||
|
autoDL = true
|
||||||
|
filename = geoipFilename
|
||||||
|
}
|
||||||
|
if autoDL {
|
||||||
|
if !l.shouldDownload(filename) {
|
||||||
|
m, err := v2geo.LoadGeoIP(filename)
|
||||||
|
if err == nil {
|
||||||
|
l.geoipMap = m
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
// file is broken, download it again
|
||||||
|
}
|
||||||
|
err := l.downloadAndCheck(filename, geoipURL, func(filename string) error {
|
||||||
|
_, err := v2geo.LoadGeoIP(filename)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// as long as the previous download exists, fallback to it
|
||||||
|
if _, serr := os.Stat(filename); os.IsNotExist(serr) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m, err := v2geo.LoadGeoIP(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l.geoipMap = m
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GeoLoader) LoadGeoSite() (map[string]*v2geo.GeoSite, error) {
|
||||||
|
if l.geositeMap != nil {
|
||||||
|
return l.geositeMap, nil
|
||||||
|
}
|
||||||
|
autoDL := false
|
||||||
|
filename := l.GeoSiteFilename
|
||||||
|
if filename == "" {
|
||||||
|
autoDL = true
|
||||||
|
filename = geositeFilename
|
||||||
|
}
|
||||||
|
if autoDL {
|
||||||
|
if !l.shouldDownload(filename) {
|
||||||
|
m, err := v2geo.LoadGeoSite(filename)
|
||||||
|
if err == nil {
|
||||||
|
l.geositeMap = m
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
// file is broken, download it again
|
||||||
|
}
|
||||||
|
err := l.downloadAndCheck(filename, geositeURL, func(filename string) error {
|
||||||
|
_, err := v2geo.LoadGeoSite(filename)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// as long as the previous download exists, fallback to it
|
||||||
|
if _, serr := os.Stat(filename); os.IsNotExist(serr) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m, err := v2geo.LoadGeoSite(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l.geositeMap = m
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GeoLoader) geoDownloadFunc(filename, url string) {
|
||||||
|
l.Logger.Info("downloading database", zap.String("filename", filename), zap.String("url", url))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GeoLoader) geoDownloadErrFunc(err error) {
|
||||||
|
if err != nil {
|
||||||
|
l.Logger.Error("failed to download database", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
33
core/hy2/hook.go
Normal file
33
core/hy2/hook.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HookServer struct {
|
||||||
|
Tag string
|
||||||
|
Counter sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HookServer) LogTraffic(id string, tx, rx uint64) (ok bool) {
|
||||||
|
var c interface{}
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
if c, exists = h.Counter.Load(h.Tag); !exists {
|
||||||
|
c = counter.NewTrafficCounter()
|
||||||
|
h.Counter.Store(h.Tag, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc, ok := c.(*counter.TrafficCounter); ok {
|
||||||
|
tc.Rx(id, int(rx))
|
||||||
|
tc.Tx(id, int(tx))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HookServer) LogOnlineState(id string, online bool) {
|
||||||
|
}
|
||||||
61
core/hy2/hy2.go
Normal file
61
core/hy2/hy2.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/InazumaV/V2bX/conf"
|
||||||
|
vCore "github.com/InazumaV/V2bX/core"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ vCore.Core = (*Hysteria2)(nil)
|
||||||
|
|
||||||
|
type Hysteria2 struct {
|
||||||
|
Hy2nodes map[string]Hysteria2node
|
||||||
|
Auth *V2bX
|
||||||
|
Logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
vCore.RegisterCore("hysteria2", New)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(c *conf.CoreConfig) (vCore.Core, error) {
|
||||||
|
loglever := "error"
|
||||||
|
if c.Hysteria2Config.LogConfig.Level != "" {
|
||||||
|
loglever = c.Hysteria2Config.LogConfig.Level
|
||||||
|
}
|
||||||
|
log, err := initLogger(loglever, "console")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Hysteria2{
|
||||||
|
Hy2nodes: make(map[string]Hysteria2node),
|
||||||
|
Auth: &V2bX{
|
||||||
|
usersMap: make(map[string]int),
|
||||||
|
},
|
||||||
|
Logger: log,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) Protocols() []string {
|
||||||
|
return []string{
|
||||||
|
"hysteria2",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) Close() error {
|
||||||
|
for _, n := range h.Hy2nodes {
|
||||||
|
err := n.Hy2server.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) Type() string {
|
||||||
|
return "hysteria2"
|
||||||
|
}
|
||||||
137
core/hy2/logger.go
Normal file
137
core/hy2/logger.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/InazumaV/V2bX/limiter"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
type serverLogger struct {
|
||||||
|
Tag string
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
var logLevelMap = map[string]zapcore.Level{
|
||||||
|
"debug": zapcore.DebugLevel,
|
||||||
|
"info": zapcore.InfoLevel,
|
||||||
|
"warn": zapcore.WarnLevel,
|
||||||
|
"error": zapcore.ErrorLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
var logFormatMap = map[string]zapcore.EncoderConfig{
|
||||||
|
"console": {
|
||||||
|
TimeKey: "time",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "logger",
|
||||||
|
MessageKey: "msg",
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||||
|
EncodeTime: zapcore.RFC3339TimeEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
TimeKey: "time",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "logger",
|
||||||
|
MessageKey: "msg",
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||||
|
EncodeTime: zapcore.EpochMillisTimeEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serverLogger) Connect(addr net.Addr, uuid string, tx uint64) {
|
||||||
|
limiter, err := limiter.GetLimiter(l.Tag)
|
||||||
|
if err != nil {
|
||||||
|
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 {
|
||||||
|
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid))
|
||||||
|
}
|
||||||
|
l.logger.Info("client connected", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint64("tx", tx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serverLogger) Disconnect(addr net.Addr, uuid string, err error) {
|
||||||
|
l.logger.Info("client disconnected", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serverLogger) TCPRequest(addr net.Addr, uuid, reqAddr string) {
|
||||||
|
limiter, err := limiter.GetLimiter(l.Tag)
|
||||||
|
if err != nil {
|
||||||
|
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 {
|
||||||
|
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid))
|
||||||
|
}
|
||||||
|
l.logger.Debug("TCP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.String("reqAddr", reqAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serverLogger) TCPError(addr net.Addr, uuid, reqAddr string, err error) {
|
||||||
|
if err == nil {
|
||||||
|
l.logger.Debug("TCP closed", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.String("reqAddr", reqAddr))
|
||||||
|
} else {
|
||||||
|
l.logger.Debug("TCP error", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.String("reqAddr", reqAddr), zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serverLogger) UDPRequest(addr net.Addr, uuid string, sessionId uint32, reqAddr string) {
|
||||||
|
limiter, err := limiter.GetLimiter(l.Tag)
|
||||||
|
if err != nil {
|
||||||
|
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 {
|
||||||
|
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid))
|
||||||
|
}
|
||||||
|
l.logger.Debug("UDP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint32("sessionId", sessionId), zap.String("reqAddr", reqAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serverLogger) UDPError(addr net.Addr, uuid string, sessionId uint32, err error) {
|
||||||
|
if err == nil {
|
||||||
|
l.logger.Debug("UDP closed", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint32("sessionId", sessionId))
|
||||||
|
} else {
|
||||||
|
l.logger.Debug("UDP error", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint32("sessionId", sessionId), zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initLogger(logLevel string, logFormat string) (*zap.Logger, error) {
|
||||||
|
level, ok := logLevelMap[strings.ToLower(logLevel)]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(fmt.Sprintf("unsupported log level: %s\n", logLevel))
|
||||||
|
}
|
||||||
|
enc, ok := logFormatMap[strings.ToLower(logFormat)]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(fmt.Sprintf("unsupported log format: %s\n", logFormat))
|
||||||
|
}
|
||||||
|
c := zap.Config{
|
||||||
|
Level: zap.NewAtomicLevelAt(level),
|
||||||
|
DisableCaller: true,
|
||||||
|
DisableStacktrace: true,
|
||||||
|
Encoding: strings.ToLower(logFormat),
|
||||||
|
EncoderConfig: enc,
|
||||||
|
OutputPaths: []string{"stderr"},
|
||||||
|
ErrorOutputPaths: []string{"stderr"},
|
||||||
|
}
|
||||||
|
logger, err := c.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(fmt.Sprintf("failed to initialize logger: %s\n", err))
|
||||||
|
}
|
||||||
|
return logger, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractIPFromAddr(addr net.Addr) string {
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return v.IP.String()
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return v.IP.String()
|
||||||
|
case *net.IPAddr:
|
||||||
|
return v.IP.String()
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
71
core/hy2/node.go
Normal file
71
core/hy2/node.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
|
"github.com/InazumaV/V2bX/conf"
|
||||||
|
"github.com/apernet/hysteria/core/v2/server"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Hysteria2node struct {
|
||||||
|
Hy2server server.Server
|
||||||
|
Tag string
|
||||||
|
Logger *zap.Logger
|
||||||
|
EventLogger server.EventLogger
|
||||||
|
TrafficLogger server.TrafficLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error {
|
||||||
|
var err error
|
||||||
|
hyconfig := &server.Config{}
|
||||||
|
var c serverConfig
|
||||||
|
v := viper.New()
|
||||||
|
if len(config.Hysteria2ConfigPath) != 0 {
|
||||||
|
v.SetConfigFile(config.Hysteria2ConfigPath)
|
||||||
|
if err := v.ReadInConfig(); err != nil {
|
||||||
|
h.Logger.Fatal("failed to read server config", zap.Error(err))
|
||||||
|
}
|
||||||
|
if err := v.Unmarshal(&c); err != nil {
|
||||||
|
h.Logger.Fatal("failed to parse server config", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := Hysteria2node{
|
||||||
|
Tag: tag,
|
||||||
|
Logger: h.Logger,
|
||||||
|
EventLogger: &serverLogger{
|
||||||
|
Tag: tag,
|
||||||
|
logger: h.Logger,
|
||||||
|
},
|
||||||
|
TrafficLogger: &HookServer{
|
||||||
|
Tag: tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
hyconfig, err = n.getHyConfig(info, config, &c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hyconfig.Authenticator = h.Auth
|
||||||
|
s, err := server.NewServer(hyconfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n.Hy2server = s
|
||||||
|
h.Hy2nodes[tag] = n
|
||||||
|
go func() {
|
||||||
|
if err := s.Serve(); err != nil {
|
||||||
|
h.Logger.Error("Server Error", zap.Error(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) DelNode(tag string) error {
|
||||||
|
err := h.Hy2nodes[tag].Hy2server.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
delete(h.Hy2nodes, tag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
228
core/hy2/serverConfig.go
Normal file
228
core/hy2/serverConfig.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/extras/v2/outbounds"
|
||||||
|
)
|
||||||
|
|
||||||
|
type serverConfig struct {
|
||||||
|
Listen string `mapstructure:"listen"`
|
||||||
|
Obfs serverConfigObfs `mapstructure:"obfs"`
|
||||||
|
TLS *serverConfigTLS `mapstructure:"tls"`
|
||||||
|
ACME *serverConfigACME `mapstructure:"acme"`
|
||||||
|
QUIC serverConfigQUIC `mapstructure:"quic"`
|
||||||
|
Bandwidth serverConfigBandwidth `mapstructure:"bandwidth"`
|
||||||
|
IgnoreClientBandwidth bool `mapstructure:"ignoreClientBandwidth"`
|
||||||
|
DisableUDP bool `mapstructure:"disableUDP"`
|
||||||
|
UDPIdleTimeout time.Duration `mapstructure:"udpIdleTimeout"`
|
||||||
|
Auth serverConfigAuth `mapstructure:"auth"`
|
||||||
|
Resolver serverConfigResolver `mapstructure:"resolver"`
|
||||||
|
ACL serverConfigACL `mapstructure:"acl"`
|
||||||
|
Outbounds []serverConfigOutboundEntry `mapstructure:"outbounds"`
|
||||||
|
TrafficStats serverConfigTrafficStats `mapstructure:"trafficStats"`
|
||||||
|
Masquerade serverConfigMasquerade `mapstructure:"masquerade"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigObfsSalamander struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigObfs struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
Salamander serverConfigObfsSalamander `mapstructure:"salamander"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigTLS struct {
|
||||||
|
Cert string `mapstructure:"cert"`
|
||||||
|
Key string `mapstructure:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigACME struct {
|
||||||
|
Domains []string `mapstructure:"domains"`
|
||||||
|
Email string `mapstructure:"email"`
|
||||||
|
CA string `mapstructure:"ca"`
|
||||||
|
DisableHTTP bool `mapstructure:"disableHTTP"`
|
||||||
|
DisableTLSALPN bool `mapstructure:"disableTLSALPN"`
|
||||||
|
AltHTTPPort int `mapstructure:"altHTTPPort"`
|
||||||
|
AltTLSALPNPort int `mapstructure:"altTLSALPNPort"`
|
||||||
|
Dir string `mapstructure:"dir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigQUIC struct {
|
||||||
|
InitStreamReceiveWindow uint64 `mapstructure:"initStreamReceiveWindow"`
|
||||||
|
MaxStreamReceiveWindow uint64 `mapstructure:"maxStreamReceiveWindow"`
|
||||||
|
InitConnectionReceiveWindow uint64 `mapstructure:"initConnReceiveWindow"`
|
||||||
|
MaxConnectionReceiveWindow uint64 `mapstructure:"maxConnReceiveWindow"`
|
||||||
|
MaxIdleTimeout time.Duration `mapstructure:"maxIdleTimeout"`
|
||||||
|
MaxIncomingStreams int64 `mapstructure:"maxIncomingStreams"`
|
||||||
|
DisablePathMTUDiscovery bool `mapstructure:"disablePathMTUDiscovery"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigBandwidth struct {
|
||||||
|
Up string `mapstructure:"up"`
|
||||||
|
Down string `mapstructure:"down"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigAuthHTTP struct {
|
||||||
|
URL string `mapstructure:"url"`
|
||||||
|
Insecure bool `mapstructure:"insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigAuth struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
UserPass map[string]string `mapstructure:"userpass"`
|
||||||
|
HTTP serverConfigAuthHTTP `mapstructure:"http"`
|
||||||
|
Command string `mapstructure:"command"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigResolverTCP struct {
|
||||||
|
Addr string `mapstructure:"addr"`
|
||||||
|
Timeout time.Duration `mapstructure:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigResolverUDP struct {
|
||||||
|
Addr string `mapstructure:"addr"`
|
||||||
|
Timeout time.Duration `mapstructure:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigResolverTLS struct {
|
||||||
|
Addr string `mapstructure:"addr"`
|
||||||
|
Timeout time.Duration `mapstructure:"timeout"`
|
||||||
|
SNI string `mapstructure:"sni"`
|
||||||
|
Insecure bool `mapstructure:"insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigResolverHTTPS struct {
|
||||||
|
Addr string `mapstructure:"addr"`
|
||||||
|
Timeout time.Duration `mapstructure:"timeout"`
|
||||||
|
SNI string `mapstructure:"sni"`
|
||||||
|
Insecure bool `mapstructure:"insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigResolver struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
TCP serverConfigResolverTCP `mapstructure:"tcp"`
|
||||||
|
UDP serverConfigResolverUDP `mapstructure:"udp"`
|
||||||
|
TLS serverConfigResolverTLS `mapstructure:"tls"`
|
||||||
|
HTTPS serverConfigResolverHTTPS `mapstructure:"https"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigACL struct {
|
||||||
|
File string `mapstructure:"file"`
|
||||||
|
Inline []string `mapstructure:"inline"`
|
||||||
|
GeoIP string `mapstructure:"geoip"`
|
||||||
|
GeoSite string `mapstructure:"geosite"`
|
||||||
|
GeoUpdateInterval time.Duration `mapstructure:"geoUpdateInterval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigOutboundDirect struct {
|
||||||
|
Mode string `mapstructure:"mode"`
|
||||||
|
BindIPv4 string `mapstructure:"bindIPv4"`
|
||||||
|
BindIPv6 string `mapstructure:"bindIPv6"`
|
||||||
|
BindDevice string `mapstructure:"bindDevice"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigOutboundSOCKS5 struct {
|
||||||
|
Addr string `mapstructure:"addr"`
|
||||||
|
Username string `mapstructure:"username"`
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigOutboundHTTP struct {
|
||||||
|
URL string `mapstructure:"url"`
|
||||||
|
Insecure bool `mapstructure:"insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigOutboundEntry struct {
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
Direct serverConfigOutboundDirect `mapstructure:"direct"`
|
||||||
|
SOCKS5 serverConfigOutboundSOCKS5 `mapstructure:"socks5"`
|
||||||
|
HTTP serverConfigOutboundHTTP `mapstructure:"http"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigTrafficStats struct {
|
||||||
|
Listen string `mapstructure:"listen"`
|
||||||
|
Secret string `mapstructure:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigMasqueradeFile struct {
|
||||||
|
Dir string `mapstructure:"dir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigMasqueradeProxy struct {
|
||||||
|
URL string `mapstructure:"url"`
|
||||||
|
RewriteHost bool `mapstructure:"rewriteHost"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigMasqueradeString struct {
|
||||||
|
Content string `mapstructure:"content"`
|
||||||
|
Headers map[string]string `mapstructure:"headers"`
|
||||||
|
StatusCode int `mapstructure:"statusCode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverConfigMasquerade struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
File serverConfigMasqueradeFile `mapstructure:"file"`
|
||||||
|
Proxy serverConfigMasqueradeProxy `mapstructure:"proxy"`
|
||||||
|
String serverConfigMasqueradeString `mapstructure:"string"`
|
||||||
|
ListenHTTP string `mapstructure:"listenHTTP"`
|
||||||
|
ListenHTTPS string `mapstructure:"listenHTTPS"`
|
||||||
|
ForceHTTPS bool `mapstructure:"forceHTTPS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverConfigOutboundDirectToOutbound(c serverConfigOutboundDirect) (outbounds.PluggableOutbound, error) {
|
||||||
|
var mode outbounds.DirectOutboundMode
|
||||||
|
switch strings.ToLower(c.Mode) {
|
||||||
|
case "", "auto":
|
||||||
|
mode = outbounds.DirectOutboundModeAuto
|
||||||
|
case "64":
|
||||||
|
mode = outbounds.DirectOutboundMode64
|
||||||
|
case "46":
|
||||||
|
mode = outbounds.DirectOutboundMode46
|
||||||
|
case "6":
|
||||||
|
mode = outbounds.DirectOutboundMode6
|
||||||
|
case "4":
|
||||||
|
mode = outbounds.DirectOutboundMode4
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("outbounds.direct.mode unsupported mode")
|
||||||
|
}
|
||||||
|
bindIP := len(c.BindIPv4) > 0 || len(c.BindIPv6) > 0
|
||||||
|
bindDevice := len(c.BindDevice) > 0
|
||||||
|
if bindIP && bindDevice {
|
||||||
|
return nil, fmt.Errorf("outbounds.direct cannot bind both IP and device")
|
||||||
|
}
|
||||||
|
if bindIP {
|
||||||
|
ip4, ip6 := net.ParseIP(c.BindIPv4), net.ParseIP(c.BindIPv6)
|
||||||
|
if len(c.BindIPv4) > 0 && ip4 == nil {
|
||||||
|
return nil, fmt.Errorf("outbounds.direct.bindIPv4 invalid IPv4 address")
|
||||||
|
}
|
||||||
|
if len(c.BindIPv6) > 0 && ip6 == nil {
|
||||||
|
return nil, fmt.Errorf("outbounds.direct.bindIPv6 invalid IPv6 address")
|
||||||
|
}
|
||||||
|
return outbounds.NewDirectOutboundBindToIPs(mode, ip4, ip6)
|
||||||
|
}
|
||||||
|
if bindDevice {
|
||||||
|
return outbounds.NewDirectOutboundBindToDevice(mode, c.BindDevice)
|
||||||
|
}
|
||||||
|
return outbounds.NewDirectOutboundSimple(mode), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverConfigOutboundSOCKS5ToOutbound(c serverConfigOutboundSOCKS5) (outbounds.PluggableOutbound, error) {
|
||||||
|
if c.Addr == "" {
|
||||||
|
return nil, fmt.Errorf("outbounds.socks5.addr empty socks5 address")
|
||||||
|
}
|
||||||
|
return outbounds.NewSOCKS5Outbound(c.Addr, c.Username, c.Password), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverConfigOutboundHTTPToOutbound(c serverConfigOutboundHTTP) (outbounds.PluggableOutbound, error) {
|
||||||
|
if c.URL == "" {
|
||||||
|
return nil, fmt.Errorf("outbounds.http.url empty http address")
|
||||||
|
}
|
||||||
|
return outbounds.NewHTTPOutbound(c.URL, c.Insecure)
|
||||||
|
}
|
||||||
70
core/hy2/user.go
Normal file
70
core/hy2/user.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package hy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
|
vCore "github.com/InazumaV/V2bX/core"
|
||||||
|
"github.com/apernet/hysteria/core/v2/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ server.Authenticator = &V2bX{}
|
||||||
|
|
||||||
|
type V2bX struct {
|
||||||
|
usersMap map[string]int
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *V2bX) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) {
|
||||||
|
v.mutex.Lock()
|
||||||
|
defer v.mutex.Unlock()
|
||||||
|
if _, exists := v.usersMap[auth]; exists {
|
||||||
|
return true, auth
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, user := range p.Users {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(u panel.UserInfo) {
|
||||||
|
defer wg.Done()
|
||||||
|
h.Auth.mutex.Lock()
|
||||||
|
h.Auth.usersMap[u.Uuid] = u.Id
|
||||||
|
h.Auth.mutex.Unlock()
|
||||||
|
}(user)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return len(p.Users), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) DelUsers(users []panel.UserInfo, tag string) error {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, user := range users {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(u panel.UserInfo) {
|
||||||
|
defer wg.Done()
|
||||||
|
h.Auth.mutex.Lock()
|
||||||
|
delete(h.Auth.usersMap, u.Uuid)
|
||||||
|
h.Auth.mutex.Unlock()
|
||||||
|
}(user)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria2) GetUserTraffic(tag string, uuid string, reset bool) (up int64, down int64) {
|
||||||
|
if v, ok := h.Hy2nodes[tag].TrafficLogger.(*HookServer).Counter.Load(tag); ok {
|
||||||
|
c := v.(*counter.TrafficCounter)
|
||||||
|
up = c.GetUpCount(uuid)
|
||||||
|
down = c.GetDownCount(uuid)
|
||||||
|
if reset {
|
||||||
|
c.Reset(uuid)
|
||||||
|
}
|
||||||
|
return up, down
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
5
core/imports/hy2.go
Normal file
5
core/imports/hy2.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
//go:build hysteria2
|
||||||
|
|
||||||
|
package imports
|
||||||
|
|
||||||
|
import _ "github.com/InazumaV/V2bX/core/hy2"
|
||||||
@@ -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"
|
||||||
@@ -95,7 +96,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); 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,7 +150,7 @@ 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, 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
|
||||||
|
|||||||
@@ -91,7 +91,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,
|
||||||
@@ -348,6 +348,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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -70,13 +70,15 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
err = b.inbounds[p.Tag].(*inbound.Hysteria).AddUsers(us)
|
err = b.inbounds[p.Tag].(*inbound.Hysteria).AddUsers(us)
|
||||||
case "hysteria2":
|
case "hysteria2":
|
||||||
us := make([]option.Hysteria2User, len(p.Users))
|
us := make([]option.Hysteria2User, len(p.Users))
|
||||||
|
id := make([]int, len(p.Users))
|
||||||
for i := range p.Users {
|
for i := range p.Users {
|
||||||
us[i] = option.Hysteria2User{
|
us[i] = option.Hysteria2User{
|
||||||
Name: p.Users[i].Uuid,
|
Name: p.Users[i].Uuid,
|
||||||
Password: p.Users[i].Uuid,
|
Password: p.Users[i].Uuid,
|
||||||
}
|
}
|
||||||
|
id[i] = p.Users[i].Id
|
||||||
}
|
}
|
||||||
err = b.inbounds[p.Tag].(*inbound.Hysteria2).AddUsers(us)
|
err = b.inbounds[p.Tag].(*inbound.Hysteria2).AddUsers(us, id)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|||||||
@@ -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,24 @@ 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)
|
||||||
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 +220,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()
|
||||||
@@ -231,7 +246,7 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
|
|||||||
}
|
}
|
||||||
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 +264,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 +296,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 +321,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 +350,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 +420,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 +448,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 +459,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 +490,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 +503,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 +522,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
|
||||||
|
|||||||
@@ -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{})
|
|
||||||
}
|
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -196,8 +214,9 @@ func buildV2ray(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreCon
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTrojan(config *conf.Options, inbound *coreConf.InboundDetourConfig) error {
|
func buildTrojan(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
|
||||||
inbound.Protocol = "trojan"
|
inbound.Protocol = "trojan"
|
||||||
|
v := nodeInfo.Trojan
|
||||||
if config.XrayOptions.EnableFallback {
|
if config.XrayOptions.EnableFallback {
|
||||||
// Set fallback
|
// Set fallback
|
||||||
fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)
|
fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)
|
||||||
@@ -215,8 +234,31 @@ func buildTrojan(config *conf.Options, inbound *coreConf.InboundDetourConfig) er
|
|||||||
s := []byte("{}")
|
s := []byte("{}")
|
||||||
inbound.Settings = (*json.RawMessage)(&s)
|
inbound.Settings = (*json.RawMessage)(&s)
|
||||||
}
|
}
|
||||||
t := coreConf.TransportProtocol("tcp")
|
network := v.Network
|
||||||
|
if network == "" {
|
||||||
|
network = "tcp"
|
||||||
|
}
|
||||||
|
t := coreConf.TransportProtocol(network)
|
||||||
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
||||||
|
switch 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)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("the network type is not vail")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
265
go.mod
265
go.mod
@@ -1,120 +1,155 @@
|
|||||||
module github.com/InazumaV/V2bX
|
module github.com/InazumaV/V2bX
|
||||||
|
|
||||||
go 1.21.4
|
go 1.22
|
||||||
|
|
||||||
|
toolchain go1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/apernet/hysteria/core/v2 v2.5.1-0.20240702222552-458ee1386cc4
|
||||||
|
github.com/apernet/hysteria/extras/v2 v2.5.1-0.20240702222552-458ee1386cc4
|
||||||
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.1-0.20240105061852-782bc05c5573
|
github.com/sagernet/sing v0.5.0-alpha.12
|
||||||
github.com/sagernet/sing-box v1.8.2
|
github.com/sagernet/sing-box v1.10.0
|
||||||
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/xtls/xray-core v1.8.8-0.20240120214034-53de58fad3a0
|
github.com/spf13/viper v1.15.0
|
||||||
golang.org/x/crypto v0.18.0
|
github.com/xtls/xray-core v1.8.17-0.20240708033233-ce637c0c232c
|
||||||
golang.org/x/sys v0.16.0
|
go.uber.org/zap v1.27.0
|
||||||
google.golang.org/protobuf v1.32.0
|
golang.org/x/crypto v0.25.0
|
||||||
|
golang.org/x/sys v0.22.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.23.3 // 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.0.6 // indirect
|
github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e // 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/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.3.9 // 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/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/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-20231229205709-960ae82b1e42 // 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.4.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/hcl v1.0.0 // indirect
|
||||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||||
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.4 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // 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/mattn/go-isatty v0.0.19 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
|
github.com/mdlayher/socket v0.4.1 // 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.61 // 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
|
||||||
@@ -122,103 +157,121 @@ 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.13.2 // 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/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.45.1 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.1 // 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-20231209105102-8d27a30e436e // 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.40.1-beta.2 // indirect
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||||
github.com/sagernet/sing-dns v0.1.12 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
|
github.com/sagernet/quic-go v0.45.1-beta.2 // indirect
|
||||||
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||||
|
github.com/sagernet/sing-dns v0.3.0-beta.10 // 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.7 // indirect
|
github.com/sagernet/sing-quic v0.2.0-beta.12 // 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.0 // 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/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-20220411075957-e3b120b3f5fb // indirect
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
||||||
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect
|
github.com/selectel/domains-go v1.1.0 // 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/spf13/cast v1.3.1 // indirect
|
github.com/sony/gobreaker v0.5.0 // indirect
|
||||||
|
github.com/spf13/afero v1.9.3 // indirect
|
||||||
|
github.com/spf13/cast v1.5.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/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.898 // indirect
|
||||||
github.com/transip/gotransip/v6 v6.20.0 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 // indirect
|
||||||
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
|
github.com/transip/gotransip/v6 v6.23.0 // indirect
|
||||||
|
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
|
||||||
|
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 // 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.2.1-beta.2.0.20230316163032-ced5aaba43e3 // 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-20240429224917-ecc4401070cc // 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.49.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.24.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
|
||||||
go.uber.org/zap v1.26.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-20240103183307-be819d1f06fc // indirect
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
golang.org/x/mod v0.18.0 // indirect
|
||||||
golang.org/x/net v0.20.0 // indirect
|
golang.org/x/net v0.27.0 // indirect
|
||||||
golang.org/x/oauth2 v0.13.0 // indirect
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
|
golang.org/x/text v0.16.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.17.0 // indirect
|
golang.org/x/tools v0.22.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.149.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-20231212172506-995d672761c0 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
google.golang.org/grpc v1.65.0 // indirect
|
||||||
google.golang.org/grpc v1.60.1 // 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
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/sagernet/sing-box v1.8.2 => github.com/wyx2685/sing-box_mod v0.0.0-20240122043647-0854bf9053ab
|
//github.com/apernet/hysteria/core v1.3.5-0.20240201034858-bb99579bb92c => /root/hysteria/core
|
||||||
|
replace github.com/sagernet/sing-box v1.10.0 => github.com/wyx2685/sing-box_mod v0.0.9
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type Limiter struct {
|
|||||||
type UserLimitInfo struct {
|
type UserLimitInfo struct {
|
||||||
UID int
|
UID int
|
||||||
SpeedLimit int
|
SpeedLimit int
|
||||||
|
DeviceLimit int
|
||||||
DynamicSpeedLimit int
|
DynamicSpeedLimit int
|
||||||
ExpireTime int64
|
ExpireTime int64
|
||||||
}
|
}
|
||||||
@@ -60,13 +61,15 @@ func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limite
|
|||||||
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
|
||||||
|
}
|
||||||
|
info.UserLimitInfo.Store(format.UserTag(tag, users[i].Uuid), userLimit)
|
||||||
}
|
}
|
||||||
info.UUIDtoUID = uuidmap
|
info.UUIDtoUID = uuidmap
|
||||||
limitLock.Lock()
|
limitLock.Lock()
|
||||||
@@ -97,14 +100,17 @@ 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
|
||||||
|
}
|
||||||
|
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,23 +126,27 @@ func (l *Limiter) UpdateDynamicSpeedLimit(tag, uuid string, limit int, expire ti
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Limiter) CheckLimit(email string, ip string, isTcp bool) (Bucket *ratelimit.Bucket, Reject bool) {
|
func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool) (Bucket *ratelimit.Bucket, Reject bool) {
|
||||||
// ip and conn limiter
|
// ip and conn limiter
|
||||||
if l.ConnLimiter.AddConnCount(email, 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(email); 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(email)
|
l.UserLimitInfo.Delete(taguuid)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
|
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
|
||||||
@@ -145,10 +155,9 @@ func (l *Limiter) CheckLimit(email string, ip string, isTcp bool) (Bucket *ratel
|
|||||||
|
|
||||||
// Store online user for device limit
|
// Store online user for device limit
|
||||||
ipMap := new(sync.Map)
|
ipMap := new(sync.Map)
|
||||||
uid := l.UUIDtoUID[email]
|
|
||||||
ipMap.Store(ip, uid)
|
ipMap.Store(ip, uid)
|
||||||
// If any device is online
|
// If any device is online
|
||||||
if v, ok := l.UserOnlineIP.LoadOrStore(email, 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 {
|
||||||
@@ -157,16 +166,20 @@ func (l *Limiter) CheckLimit(email string, ip string, isTcp bool) (Bucket *ratel
|
|||||||
counter++
|
counter++
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
if counter > deviceLimit && deviceLimit > 0 {
|
||||||
|
ipMap.Delete(ip)
|
||||||
|
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(email, 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(email, Bucket)
|
l.SpeedLimiter.Store(taguuid, Bucket)
|
||||||
return Bucket, false
|
return Bucket, false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -1,9 +1,16 @@
|
|||||||
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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user