mirror of
https://github.com/wyx2685/V2bX.git
synced 2026-02-04 12:40:11 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08ebbed9fb | ||
|
|
792841d073 | ||
|
|
981e59b836 | ||
|
|
7dbe5fda85 | ||
|
|
4f9ccdf8db | ||
|
|
c2d5861d7d | ||
|
|
0e29c19f0e | ||
|
|
a1c40bb1c8 | ||
|
|
a0de94efff | ||
|
|
29928a1135 | ||
|
|
ab1ca837de | ||
|
|
1f61446fa9 | ||
|
|
c0325227db | ||
|
|
ba3036a7ac | ||
|
|
f99e2b4489 | ||
|
|
0af952be10 | ||
|
|
ad5971f164 | ||
|
|
f7d5d891c3 | ||
|
|
6936a76724 | ||
|
|
7184e49650 | ||
|
|
ea0b7d8f40 | ||
|
|
12fbcb1460 | ||
|
|
c6d48e1edf | ||
|
|
8d7168c6a4 | ||
|
|
173c48a76f | ||
|
|
130e94cf45 | ||
|
|
89ddfff060 | ||
|
|
07d49293d8 | ||
|
|
9e8f87740e | ||
|
|
29a99985c8 | ||
|
|
248ff3764f | ||
|
|
3dfeba7e68 | ||
|
|
8eb623b3f0 | ||
|
|
cdcbddd464 | ||
|
|
e81d47321b | ||
|
|
4d82eff518 | ||
|
|
b96545649b | ||
|
|
8757b955a6 | ||
|
|
33d9ab4b0a | ||
|
|
a7637d436f | ||
|
|
4dda65a636 | ||
|
|
6c725b424f | ||
|
|
a052a1f1e8 | ||
|
|
a85352c402 |
22
.github/ISSUE_TEMPLATE/bug-report.md
vendored
22
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,27 +1,25 @@
|
|||||||
---
|
---
|
||||||
name: "Bug 反馈"
|
name: "Bug 反馈"
|
||||||
about: 创建一个报告以帮助我们修复并改进XrayR
|
about: 创建一个报告以帮助我们修复并改进V2bX
|
||||||
title: ''
|
title: ''
|
||||||
labels: awaiting reply, bug
|
labels:
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
**描述该错误**
|
**描述该错误**
|
||||||
简单地描述一下这个bug是什么
|
简单地描述一下这个bug是什么
|
||||||
|
|
||||||
**复现**
|
|
||||||
复现该bug的步骤
|
|
||||||
|
|
||||||
**环境和版本**
|
|
||||||
- 系统 [例如:Debian 11]
|
**复现**
|
||||||
- 架构 [例如:AMD64]
|
请自行复现,并贴出详细步骤操作过程
|
||||||
- 面板 [例如:V2board]
|
|
||||||
- 协议 [例如:vmess]
|
|
||||||
- 版本 [例如:0.8.2.2]
|
|
||||||
- 部署方式 [例如:一键脚本]
|
|
||||||
|
|
||||||
**日志和错误**
|
**日志和错误**
|
||||||
请使用`xrayr log`查看并添加日志,以帮助解释你的问题
|
请使用`v2bx log`查看并添加日志,没有日志的issue不会得到答复并且会被直接关闭
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**额外的内容**
|
**额外的内容**
|
||||||
在这里添加关于问题的任何其他内容
|
在这里添加关于问题的任何其他内容
|
||||||
19
.github/ISSUE_TEMPLATE/feature-request.md
vendored
19
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
name: "功能建议"
|
|
||||||
about: 给XrayR提出建议,让我们做得更好
|
|
||||||
title: ''
|
|
||||||
labels: awaiting reply, feature-request
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
**描述您想要的功能**
|
|
||||||
|
|
||||||
清晰简洁的功能描述。
|
|
||||||
|
|
||||||
**描述您考虑过的替代方案**
|
|
||||||
|
|
||||||
是否有任何替代方案可以解决这个问题?
|
|
||||||
|
|
||||||
**附加上下文**
|
|
||||||
|
|
||||||
在此处添加有关功能请求的任何其他上下文或截图。
|
|
||||||
12
.github/workflows/Publish Docker image.yml
vendored
12
.github/workflows/Publish Docker image.yml
vendored
@@ -1,16 +1,8 @@
|
|||||||
name: Publish Docker image
|
name: Publish Docker image
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
release:
|
||||||
branches:
|
types: [published]
|
||||||
- dev_new
|
|
||||||
paths:
|
|
||||||
- "**/*.go"
|
|
||||||
- "go.mod"
|
|
||||||
- "go.sum"
|
|
||||||
- ".github/workflows/*.yml"
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- 'dev_new'
|
- 'dev_new'
|
||||||
|
|||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
|||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Show workflow information
|
- name: Show workflow information
|
||||||
id: get_filename
|
id: get_filename
|
||||||
run: |
|
run: |
|
||||||
@@ -107,9 +107,9 @@ jobs:
|
|||||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_OUTPUT
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_OUTPUT
|
||||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.22.0'
|
go-version: '1.23.2'
|
||||||
|
|
||||||
- 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 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="
|
go build -v -o build_assets/V2bX -tags "sing xray hysteria2 with_reality_server with_quic with_grpc with_utls with_wireguard with_acme with_gvisor" -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 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="
|
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 with_gvisor" -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: |
|
||||||
@@ -167,7 +167,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mv build_assets V2bX-$ASSET_NAME
|
mv build_assets V2bX-$ASSET_NAME
|
||||||
- name: Upload files to Artifacts
|
- name: Upload files to Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}
|
name: V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Build go
|
# Build go
|
||||||
FROM golang:1.22.0-alpine AS builder
|
FROM golang:1.23.2-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 hysteria2 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 with_gvisor"
|
||||||
|
|
||||||
# Release
|
# Release
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -1,16 +1,17 @@
|
|||||||
# V2bX
|
# V2bX
|
||||||
|
|
||||||
[](https://t.me/YuzukiProjects)
|
[](https://t.me/unofficialV2board)
|
||||||
|
[](https://t.me/YuzukiProjects)
|
||||||
|
|
||||||
A V2board node server based on multi core, modified from XrayR.
|
A V2board node server based on multi core, modified from XrayR.
|
||||||
一个基于多种内核的V2board节点服务端,修改自XrayR,支持V2ay,Trojan,Shadowsocks协议。
|
一个基于多种内核的V2board节点服务端,修改自XrayR,支持V2ay,Trojan,Shadowsocks协议。
|
||||||
|
|
||||||
**注意: 本项目需要V2board版本 >= 1.7.0**
|
**注意: 本项目需要搭配[修改版V2board](https://github.com/wyx2685/v2board)**
|
||||||
|
|
||||||
## 特点
|
## 特点
|
||||||
|
|
||||||
* 永久开源且免费。
|
* 永久开源且免费。
|
||||||
* 支持Vmess/Vless, Trojan, Shadowsocks, Hysteria多种协议。
|
* 支持Vmess/Vless, Trojan, Shadowsocks, Hysteria1/2多种协议。
|
||||||
* 支持Vless和XTLS等新特性。
|
* 支持Vless和XTLS等新特性。
|
||||||
* 支持单实例对接多节点,无需重复启动。
|
* 支持单实例对接多节点,无需重复启动。
|
||||||
* 支持限制在线IP。
|
* 支持限制在线IP。
|
||||||
@@ -23,7 +24,7 @@ A V2board node server based on multi core, modified from XrayR.
|
|||||||
|
|
||||||
## 功能介绍
|
## 功能介绍
|
||||||
|
|
||||||
| 功能 | v2ray | trojan | shadowsocks | hysteria |
|
| 功能 | v2ray | trojan | shadowsocks | hysteria1/2 |
|
||||||
|-----------|-------|--------|-------------|----------|
|
|-----------|-------|--------|-------------|----------|
|
||||||
| 自动申请tls证书 | √ | √ | √ | √ |
|
| 自动申请tls证书 | √ | √ | √ | √ |
|
||||||
| 自动续签tls证书 | √ | √ | √ | √ |
|
| 自动续签tls证书 | √ | √ | √ | √ |
|
||||||
@@ -32,7 +33,7 @@ A V2board node server based on multi core, modified from XrayR.
|
|||||||
| 自定义DNS | √ | √ | √ | √ |
|
| 自定义DNS | √ | √ | √ | √ |
|
||||||
| 在线IP数限制 | √ | √ | √ | √ |
|
| 在线IP数限制 | √ | √ | √ | √ |
|
||||||
| 连接数限制 | √ | √ | √ | √ |
|
| 连接数限制 | √ | √ | √ | √ |
|
||||||
| 跨节点IP数限制 | | | | |
|
| 跨节点IP数限制 |√ |√ |√ |√ |
|
||||||
| 按照用户限速 | √ | √ | √ | √ |
|
| 按照用户限速 | √ | √ | √ | √ |
|
||||||
| 动态限速(未测试) | √ | √ | √ | √ |
|
| 动态限速(未测试) | √ | √ | √ | √ |
|
||||||
|
|
||||||
@@ -52,17 +53,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, hysteria2
|
||||||
go build -o V2bX -ldflags '-s -w' -gcflags="all=-trimpath=${PWD}" -asmflags="all=-trimpath=${PWD} -tags "xray sing"
|
go build -v -o ./V2bX -tags "xray 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 +83,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)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package panel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -10,7 +9,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/common/crypt"
|
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -186,18 +184,6 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
|
|||||||
cm = &rsp.CommonNode
|
cm = &rsp.CommonNode
|
||||||
node.VAllss = rsp
|
node.VAllss = rsp
|
||||||
node.Security = node.VAllss.Tls
|
node.Security = node.VAllss.Tls
|
||||||
if len(rsp.NetworkSettings) > 0 {
|
|
||||||
err = json.Unmarshal(rsp.NetworkSettings, &rsp.RealityConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("decode reality config error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if node.Security == Reality {
|
|
||||||
if rsp.TlsSettings.PrivateKey == "" {
|
|
||||||
key := crypt.GenX25519Private([]byte("vless" + c.Token))
|
|
||||||
rsp.TlsSettings.PrivateKey = base64.RawURLEncoding.EncodeToString(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
rsp := &ShadowsocksNode{}
|
rsp := &ShadowsocksNode{}
|
||||||
err = json.Unmarshal(r.Body(), rsp)
|
err = json.Unmarshal(r.Body(), rsp)
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ type Client struct {
|
|||||||
nodeEtag string
|
nodeEtag string
|
||||||
userEtag string
|
userEtag string
|
||||||
responseBodyHash string
|
responseBodyHash string
|
||||||
LastReportOnline map[int]int
|
UserList *UserListBody
|
||||||
|
AliveMap *AliveMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(c *conf.ApiConfig) (*Client, error) {
|
func New(c *conf.ApiConfig) (*Client, error) {
|
||||||
@@ -71,5 +72,7 @@ func New(c *conf.ApiConfig) (*Client, error) {
|
|||||||
APIHost: c.APIHost,
|
APIHost: c.APIHost,
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeId: c.NodeID,
|
NodeId: c.NodeID,
|
||||||
|
UserList: &UserListBody{},
|
||||||
|
AliveMap: &AliveMap{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ type UserInfo struct {
|
|||||||
Uuid string `json:"uuid"`
|
Uuid string `json:"uuid"`
|
||||||
SpeedLimit int `json:"speed_limit"`
|
SpeedLimit int `json:"speed_limit"`
|
||||||
DeviceLimit int `json:"device_limit"`
|
DeviceLimit int `json:"device_limit"`
|
||||||
AliveIp int `json:"alive_ip"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserListBody struct {
|
type UserListBody struct {
|
||||||
@@ -24,62 +23,58 @@ type UserListBody struct {
|
|||||||
Users []UserInfo `json:"users"`
|
Users []UserInfo `json:"users"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserList will pull user form sspanel
|
type AliveMap struct {
|
||||||
func (c *Client) GetUserList() (UserList []UserInfo, err error) {
|
Alive map[int]int `json:"alive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserList will pull user from v2board
|
||||||
|
func (c *Client) GetUserList() ([]UserInfo, error) {
|
||||||
const path = "/api/v1/server/UniProxy/user"
|
const path = "/api/v1/server/UniProxy/user"
|
||||||
r, err := c.client.R().
|
r, err := c.client.R().
|
||||||
SetHeader("If-None-Match", c.userEtag).
|
SetHeader("If-None-Match", c.userEtag).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
|
if r == nil || r.RawResponse == nil {
|
||||||
|
return nil, fmt.Errorf("received nil response or raw response")
|
||||||
|
}
|
||||||
|
defer r.RawResponse.Body.Close()
|
||||||
|
|
||||||
|
if r.StatusCode() == 304 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
if err = c.checkResponse(r, path, err); err != nil {
|
if err = c.checkResponse(r, path, err); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
userlist := &UserListBody{}
|
||||||
if r != nil {
|
if err := json.Unmarshal(r.Body(), userlist); err != nil {
|
||||||
defer func() {
|
return nil, fmt.Errorf("unmarshal user list error: %w", err)
|
||||||
if r.RawBody() != nil {
|
|
||||||
r.RawBody().Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if r.StatusCode() == 304 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("received nil response")
|
|
||||||
}
|
|
||||||
var userList *UserListBody
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("read body error: %s", err)
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(r.Body(), &userList); err != nil {
|
|
||||||
return nil, fmt.Errorf("unmarshal userlist error: %s", err)
|
|
||||||
}
|
}
|
||||||
c.userEtag = r.Header().Get("ETag")
|
c.userEtag = r.Header().Get("ETag")
|
||||||
|
return userlist.Users, nil
|
||||||
|
}
|
||||||
|
|
||||||
var userinfos []UserInfo
|
// GetUserAlive will fetch the alive_ip count for users
|
||||||
var deviceLimit, localDeviceLimit int = 0, 0
|
func (c *Client) GetUserAlive() (map[int]int, error) {
|
||||||
for _, user := range userList.Users {
|
c.AliveMap = &AliveMap{}
|
||||||
// If there is still device available, add the user
|
const path = "/api/v1/server/UniProxy/alivelist"
|
||||||
if user.DeviceLimit > 0 && user.AliveIp > 0 {
|
r, err := c.client.R().
|
||||||
lastOnline := 0
|
ForceContentType("application/json").
|
||||||
if v, ok := c.LastReportOnline[user.Id]; ok {
|
Get(path)
|
||||||
lastOnline = v
|
if err != nil || r.StatusCode() >= 399 {
|
||||||
}
|
c.AliveMap.Alive = make(map[int]int)
|
||||||
// If there are any available device.
|
}
|
||||||
localDeviceLimit = user.DeviceLimit - user.AliveIp + lastOnline
|
if r == nil || r.RawResponse == nil {
|
||||||
if localDeviceLimit > 0 {
|
fmt.Printf("received nil response or raw response")
|
||||||
deviceLimit = localDeviceLimit
|
c.AliveMap.Alive = make(map[int]int)
|
||||||
} else if lastOnline > 0 {
|
}
|
||||||
deviceLimit = lastOnline
|
defer r.RawResponse.Body.Close()
|
||||||
} else {
|
if err := json.Unmarshal(r.Body(), c.AliveMap); err != nil {
|
||||||
continue
|
fmt.Printf("unmarshal user alive list error: %s", err)
|
||||||
}
|
c.AliveMap.Alive = make(map[int]int)
|
||||||
}
|
|
||||||
user.DeviceLimit = deviceLimit
|
|
||||||
userinfos = append(userinfos, user)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return userinfos, nil
|
return c.AliveMap.Alive, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserTraffic struct {
|
type UserTraffic struct {
|
||||||
@@ -106,8 +101,7 @@ func (c *Client) ReportUserTraffic(userTraffic []UserTraffic) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ReportNodeOnlineUsers(data *map[int][]string, reportOnline *map[int]int) error {
|
func (c *Client) ReportNodeOnlineUsers(data *map[int][]string) error {
|
||||||
c.LastReportOnline = *reportOnline
|
|
||||||
const path = "/api/v1/server/UniProxy/alive"
|
const path = "/api/v1/server/UniProxy/alive"
|
||||||
r, err := c.client.R().
|
r, err := c.client.R().
|
||||||
SetBody(data).
|
SetBody(data).
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TrafficCounter struct {
|
type TrafficCounter struct {
|
||||||
counters map[string]*TrafficStorage
|
counters sync.Map
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrafficStorage struct {
|
type TrafficStorage struct {
|
||||||
@@ -16,60 +15,52 @@ type TrafficStorage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewTrafficCounter() *TrafficCounter {
|
func NewTrafficCounter() *TrafficCounter {
|
||||||
return &TrafficCounter{
|
return &TrafficCounter{}
|
||||||
counters: map[string]*TrafficStorage{},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) GetCounter(id string) *TrafficStorage {
|
func (c *TrafficCounter) GetCounter(id string) *TrafficStorage {
|
||||||
c.lock.RLock()
|
if cts, ok := c.counters.Load(id); ok {
|
||||||
cts, ok := c.counters[id]
|
return cts.(*TrafficStorage)
|
||||||
c.lock.RUnlock()
|
|
||||||
if !ok {
|
|
||||||
cts = &TrafficStorage{}
|
|
||||||
c.counters[id] = cts
|
|
||||||
}
|
}
|
||||||
return cts
|
newStorage := &TrafficStorage{}
|
||||||
|
if cts, loaded := c.counters.LoadOrStore(id, newStorage); loaded {
|
||||||
|
return cts.(*TrafficStorage)
|
||||||
|
}
|
||||||
|
return newStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) GetUpCount(id string) int64 {
|
func (c *TrafficCounter) GetUpCount(id string) int64 {
|
||||||
c.lock.RLock()
|
if cts, ok := c.counters.Load(id); ok {
|
||||||
cts, ok := c.counters[id]
|
return cts.(*TrafficStorage).UpCounter.Load()
|
||||||
c.lock.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return cts.UpCounter.Load()
|
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) GetDownCount(id string) int64 {
|
func (c *TrafficCounter) GetDownCount(id string) int64 {
|
||||||
c.lock.RLock()
|
if cts, ok := c.counters.Load(id); ok {
|
||||||
cts, ok := c.counters[id]
|
return cts.(*TrafficStorage).DownCounter.Load()
|
||||||
c.lock.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return cts.DownCounter.Load()
|
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Len() int {
|
func (c *TrafficCounter) Len() int {
|
||||||
c.lock.RLock()
|
length := 0
|
||||||
defer c.lock.RUnlock()
|
c.counters.Range(func(_, _ interface{}) bool {
|
||||||
return len(c.counters)
|
length++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return length
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Reset(id string) {
|
func (c *TrafficCounter) Reset(id string) {
|
||||||
c.lock.RLock()
|
if cts, ok := c.counters.Load(id); ok {
|
||||||
cts := c.GetCounter(id)
|
cts.(*TrafficStorage).UpCounter.Store(0)
|
||||||
c.lock.RUnlock()
|
cts.(*TrafficStorage).DownCounter.Store(0)
|
||||||
cts.UpCounter.Store(0)
|
}
|
||||||
cts.DownCounter.Store(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Delete(id string) {
|
func (c *TrafficCounter) Delete(id string) {
|
||||||
c.lock.Lock()
|
c.counters.Delete(id)
|
||||||
delete(c.counters, id)
|
|
||||||
c.lock.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Rx(id string, n int) {
|
func (c *TrafficCounter) Rx(id string, n int) {
|
||||||
@@ -81,11 +72,3 @@ func (c *TrafficCounter) Tx(id string, n int) {
|
|||||||
cts := c.GetCounter(id)
|
cts := c.GetCounter(id)
|
||||||
cts.UpCounter.Add(int64(n))
|
cts.UpCounter.Add(int64(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) IncConn(auth string) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *TrafficCounter) DecConn(auth string) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/juju/ratelimit"
|
"github.com/juju/ratelimit"
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
"github.com/sagernet/sing/common/network"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewConnRateLimiter(c net.Conn, l *ratelimit.Bucket) *Conn {
|
func NewConnRateLimiter(c net.Conn, l *ratelimit.Bucket) *Conn {
|
||||||
@@ -31,6 +28,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
|||||||
return c.Conn.Write(b)
|
return c.Conn.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
type PacketConnCounter struct {
|
type PacketConnCounter struct {
|
||||||
network.PacketConn
|
network.PacketConn
|
||||||
limiter *ratelimit.Bucket
|
limiter *ratelimit.Bucket
|
||||||
@@ -47,10 +45,11 @@ func (p *PacketConnCounter) ReadPacket(buff *buf.Buffer) (destination M.Socksadd
|
|||||||
pLen := buff.Len()
|
pLen := buff.Len()
|
||||||
destination, err = p.PacketConn.ReadPacket(buff)
|
destination, err = p.PacketConn.ReadPacket(buff)
|
||||||
p.limiter.Wait(int64(buff.Len() - pLen))
|
p.limiter.Wait(int64(buff.Len() - pLen))
|
||||||
return
|
return destination, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PacketConnCounter) WritePacket(buff *buf.Buffer, destination M.Socksaddr) (err error) {
|
func (p *PacketConnCounter) WritePacket(buff *buf.Buffer, destination M.Socksaddr) (err error) {
|
||||||
p.limiter.Wait(int64(buff.Len()))
|
p.limiter.Wait(int64(buff.Len()))
|
||||||
return p.PacketConn.WritePacket(buff, destination)
|
return p.PacketConn.WritePacket(buff, destination)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
19
conf/sing.go
19
conf/sing.go
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,13 +33,13 @@ func NewSingConfig() *SingConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SingOptions struct {
|
type SingOptions struct {
|
||||||
EnableProxyProtocol bool `json:"EnableProxyProtocol"`
|
|
||||||
TCPFastOpen bool `json:"EnableTFO"`
|
TCPFastOpen bool `json:"EnableTFO"`
|
||||||
SniffEnabled bool `json:"EnableSniff"`
|
SniffEnabled bool `json:"EnableSniff"`
|
||||||
|
SniffOverrideDestination bool `json:"SniffOverrideDestination"`
|
||||||
EnableDNS bool `json:"EnableDNS"`
|
EnableDNS bool `json:"EnableDNS"`
|
||||||
DomainStrategy option.DomainStrategy `json:"DomainStrategy"`
|
DomainStrategy option.DomainStrategy `json:"DomainStrategy"`
|
||||||
SniffOverrideDestination bool `json:"SniffOverrideDestination"`
|
|
||||||
FallBackConfigs *FallBackConfigForSing `json:"FallBackConfigs"`
|
FallBackConfigs *FallBackConfigForSing `json:"FallBackConfigs"`
|
||||||
|
Multiplex *MultiplexConfig `json:"MultiplexConfig"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SingNtpConfig struct {
|
type SingNtpConfig struct {
|
||||||
@@ -60,13 +59,25 @@ type FallBack struct {
|
|||||||
ServerPort string `json:"ServerPort"`
|
ServerPort string `json:"ServerPort"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MultiplexConfig struct {
|
||||||
|
Enabled bool `json:"Enable"`
|
||||||
|
Padding bool `json:"Padding"`
|
||||||
|
Brutal BrutalOptions `json:"Brutal"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BrutalOptions struct {
|
||||||
|
Enabled bool `json:"Enable"`
|
||||||
|
UpMbps int `json:"UpMbps"`
|
||||||
|
DownMbps int `json:"DownMbps"`
|
||||||
|
}
|
||||||
|
|
||||||
func NewSingOptions() *SingOptions {
|
func NewSingOptions() *SingOptions {
|
||||||
return &SingOptions{
|
return &SingOptions{
|
||||||
EnableDNS: false,
|
EnableDNS: false,
|
||||||
EnableProxyProtocol: false,
|
|
||||||
TCPFastOpen: false,
|
TCPFastOpen: false,
|
||||||
SniffEnabled: true,
|
SniffEnabled: true,
|
||||||
SniffOverrideDestination: true,
|
SniffOverrideDestination: true,
|
||||||
FallBackConfigs: &FallBackConfigForSing{},
|
FallBackConfigs: &FallBackConfigForSing{},
|
||||||
|
Multiplex: &MultiplexConfig{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import (
|
|||||||
|
|
||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
"github.com/apernet/hysteria/core/server"
|
"github.com/apernet/hysteria/core/v2/server"
|
||||||
"github.com/apernet/hysteria/extras/correctnet"
|
"github.com/apernet/hysteria/extras/v2/correctnet"
|
||||||
"github.com/apernet/hysteria/extras/masq"
|
"github.com/apernet/hysteria/extras/v2/masq"
|
||||||
"github.com/apernet/hysteria/extras/obfs"
|
"github.com/apernet/hysteria/extras/v2/obfs"
|
||||||
"github.com/apernet/hysteria/extras/outbounds"
|
"github.com/apernet/hysteria/extras/v2/outbounds"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -284,15 +284,23 @@ func (n *Hysteria2node) getMasqHandler(tlsconfig *server.TLSConfig, conn net.Pac
|
|||||||
}
|
}
|
||||||
u, err := url.Parse(c.Masquerade.Proxy.URL)
|
u, err := url.Parse(c.Masquerade.Proxy.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(fmt.Sprintf("masquerade.proxy.url %s", err))
|
return nil, fmt.Errorf("masquerade.proxy.url %s", err)
|
||||||
}
|
}
|
||||||
handler = &httputil.ReverseProxy{
|
handler = &httputil.ReverseProxy{
|
||||||
Rewrite: func(r *httputil.ProxyRequest) {
|
Director: func(req *http.Request) {
|
||||||
r.SetURL(u)
|
req.URL.Scheme = u.Scheme
|
||||||
// SetURL rewrites the Host header,
|
req.URL.Host = u.Host
|
||||||
// but we don't want that if rewriteHost is false
|
|
||||||
|
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||||
|
xff := req.Header.Get("X-Forwarded-For")
|
||||||
|
if xff != "" {
|
||||||
|
clientIP = xff + ", " + clientIP
|
||||||
|
}
|
||||||
|
req.Header.Set("X-Forwarded-For", clientIP)
|
||||||
|
}
|
||||||
|
|
||||||
if !c.Masquerade.Proxy.RewriteHost {
|
if !c.Masquerade.Proxy.RewriteHost {
|
||||||
r.Out.Host = r.In.Host
|
req.Host = req.URL.Host
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apernet/hysteria/extras/outbounds/acl"
|
"github.com/apernet/hysteria/extras/v2/outbounds/acl"
|
||||||
"github.com/apernet/hysteria/extras/outbounds/acl/v2geo"
|
"github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,46 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/common/counter"
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
|
"github.com/InazumaV/V2bX/common/format"
|
||||||
|
"github.com/InazumaV/V2bX/limiter"
|
||||||
|
"github.com/apernet/hysteria/core/v2/server"
|
||||||
|
quic "github.com/apernet/quic-go"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ server.TrafficLogger = (*HookServer)(nil)
|
||||||
|
|
||||||
type HookServer struct {
|
type HookServer struct {
|
||||||
Tag string
|
Tag string
|
||||||
|
logger *zap.Logger
|
||||||
Counter sync.Map
|
Counter sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HookServer) TraceStream(stream quic.Stream, stats *server.StreamStats) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HookServer) UntraceStream(stream quic.Stream) {
|
||||||
|
}
|
||||||
|
|
||||||
func (h *HookServer) LogTraffic(id string, tx, rx uint64) (ok bool) {
|
func (h *HookServer) LogTraffic(id string, tx, rx uint64) (ok bool) {
|
||||||
var c interface{}
|
var c interface{}
|
||||||
var exists bool
|
var exists bool
|
||||||
|
|
||||||
|
limiterinfo, err := limiter.GetLimiter(h.Tag)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("Get limiter error", zap.String("tag", h.Tag), zap.Error(err))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(h.Tag, id))
|
||||||
|
if ok {
|
||||||
|
userlimitInfo := userLimit.(*limiter.UserLimitInfo)
|
||||||
|
if userlimitInfo.OverLimit {
|
||||||
|
userlimitInfo.OverLimit = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c, exists = h.Counter.Load(h.Tag); !exists {
|
if c, exists = h.Counter.Load(h.Tag); !exists {
|
||||||
c = counter.NewTrafficCounter()
|
c = counter.NewTrafficCounter()
|
||||||
h.Counter.Store(h.Tag, c)
|
h.Counter.Store(h.Tag, c)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/InazumaV/V2bX/common/format"
|
||||||
"github.com/InazumaV/V2bX/limiter"
|
"github.com/InazumaV/V2bX/limiter"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
@@ -46,12 +47,18 @@ var logFormatMap = map[string]zapcore.EncoderConfig{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *serverLogger) Connect(addr net.Addr, uuid string, tx uint64) {
|
func (l *serverLogger) Connect(addr net.Addr, uuid string, tx uint64) {
|
||||||
limiter, err := limiter.GetLimiter(l.Tag)
|
limiterinfo, err := limiter.GetLimiter(l.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
|
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
|
||||||
}
|
}
|
||||||
if _, r := limiter.CheckLimit(uuid, extractIPFromAddr(addr), addr.Network() == "tcp"); r {
|
if _, r := limiterinfo.CheckLimit(format.UserTag(l.Tag, uuid), extractIPFromAddr(addr), addr.Network() == "tcp", true); r {
|
||||||
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid))
|
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
|
||||||
|
userLimit.(*limiter.UserLimitInfo).OverLimit = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
|
||||||
|
userLimit.(*limiter.UserLimitInfo).OverLimit = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
l.logger.Info("client connected", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint64("tx", tx))
|
l.logger.Info("client connected", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint64("tx", tx))
|
||||||
}
|
}
|
||||||
@@ -61,12 +68,18 @@ func (l *serverLogger) Disconnect(addr net.Addr, uuid string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *serverLogger) TCPRequest(addr net.Addr, uuid, reqAddr string) {
|
func (l *serverLogger) TCPRequest(addr net.Addr, uuid, reqAddr string) {
|
||||||
limiter, err := limiter.GetLimiter(l.Tag)
|
limiterinfo, err := limiter.GetLimiter(l.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
|
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
|
||||||
}
|
}
|
||||||
if _, r := limiter.CheckLimit(uuid, extractIPFromAddr(addr), addr.Network() == "tcp"); r {
|
if _, r := limiterinfo.CheckLimit(format.UserTag(l.Tag, uuid), extractIPFromAddr(addr), addr.Network() == "tcp", true); r {
|
||||||
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid))
|
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
|
||||||
|
userLimit.(*limiter.UserLimitInfo).OverLimit = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
|
||||||
|
userLimit.(*limiter.UserLimitInfo).OverLimit = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
l.logger.Debug("TCP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.String("reqAddr", reqAddr))
|
l.logger.Debug("TCP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.String("reqAddr", reqAddr))
|
||||||
}
|
}
|
||||||
@@ -80,12 +93,18 @@ func (l *serverLogger) TCPError(addr net.Addr, uuid, reqAddr string, err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *serverLogger) UDPRequest(addr net.Addr, uuid string, sessionId uint32, reqAddr string) {
|
func (l *serverLogger) UDPRequest(addr net.Addr, uuid string, sessionId uint32, reqAddr string) {
|
||||||
limiter, err := limiter.GetLimiter(l.Tag)
|
limiterinfo, err := limiter.GetLimiter(l.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
|
l.logger.Panic("Get limiter error", zap.String("tag", l.Tag), zap.Error(err))
|
||||||
}
|
}
|
||||||
if _, r := limiter.CheckLimit(uuid, extractIPFromAddr(addr), addr.Network() == "tcp"); r {
|
if _, r := limiterinfo.CheckLimit(format.UserTag(l.Tag, uuid), extractIPFromAddr(addr), addr.Network() == "tcp", true); r {
|
||||||
l.logger.Warn("Need Reject", zap.String("addr", addr.String()), zap.String("uuid", uuid))
|
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
|
||||||
|
userLimit.(*limiter.UserLimitInfo).OverLimit = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if userLimit, ok := limiterinfo.UserLimitInfo.Load(format.UserTag(l.Tag, uuid)); ok {
|
||||||
|
userLimit.(*limiter.UserLimitInfo).OverLimit = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
l.logger.Debug("UDP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint32("sessionId", sessionId), zap.String("reqAddr", reqAddr))
|
l.logger.Debug("UDP request", zap.String("addr", addr.String()), zap.String("uuid", uuid), zap.Uint32("sessionId", sessionId), zap.String("reqAddr", reqAddr))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package hy2
|
package hy2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
"github.com/apernet/hysteria/core/server"
|
"github.com/apernet/hysteria/core/v2/server"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -38,7 +40,8 @@ func (h *Hysteria2) AddNode(tag string, info *panel.NodeInfo, config *conf.Optio
|
|||||||
logger: h.Logger,
|
logger: h.Logger,
|
||||||
},
|
},
|
||||||
TrafficLogger: &HookServer{
|
TrafficLogger: &HookServer{
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
|
logger: h.Logger,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +58,9 @@ func (h *Hysteria2) AddNode(tag string, info *panel.NodeInfo, config *conf.Optio
|
|||||||
h.Hy2nodes[tag] = n
|
h.Hy2nodes[tag] = n
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.Serve(); err != nil {
|
if err := s.Serve(); err != nil {
|
||||||
h.Logger.Error("Server Error", zap.Error(err))
|
if !strings.Contains(err.Error(), "quic: server closed") {
|
||||||
|
h.Logger.Error("Server Error", zap.Error(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apernet/hysteria/extras/outbounds"
|
"github.com/apernet/hysteria/extras/v2/outbounds"
|
||||||
)
|
)
|
||||||
|
|
||||||
type serverConfig struct {
|
type serverConfig struct {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
"github.com/InazumaV/V2bX/common/counter"
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
vCore "github.com/InazumaV/V2bX/core"
|
vCore "github.com/InazumaV/V2bX/core"
|
||||||
"github.com/apernet/hysteria/core/server"
|
"github.com/apernet/hysteria/core/v2/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ server.Authenticator = &V2bX{}
|
var _ server.Authenticator = &V2bX{}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/common/format"
|
"github.com/InazumaV/V2bX/common/format"
|
||||||
"github.com/InazumaV/V2bX/common/rate"
|
"github.com/InazumaV/V2bX/common/rate"
|
||||||
|
|
||||||
@@ -19,6 +17,8 @@ import (
|
|||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ adapter.ConnectionTracker = (*HookServer)(nil)
|
||||||
|
|
||||||
type HookServer struct {
|
type HookServer struct {
|
||||||
EnableConnClear bool
|
EnableConnClear bool
|
||||||
counter sync.Map
|
counter sync.Map
|
||||||
@@ -57,57 +57,29 @@ func (h *HookServer) ModeList() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHookServer(enableClear bool) *HookServer {
|
func NewHookServer(enableClear bool) *HookServer {
|
||||||
return &HookServer{
|
server := &HookServer{
|
||||||
EnableConnClear: enableClear,
|
EnableConnClear: enableClear,
|
||||||
counter: sync.Map{},
|
counter: sync.Map{},
|
||||||
connClears: sync.Map{},
|
connClears: sync.Map{},
|
||||||
}
|
}
|
||||||
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HookServer) Start() error {
|
func (h *HookServer) RoutedConnection(_ context.Context, conn net.Conn, m adapter.InboundContext, _ adapter.Rule, _ adapter.Outbound) net.Conn {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HookServer) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HookServer) PreStart() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HookServer) RoutedConnection(_ context.Context, conn net.Conn, m adapter.InboundContext, _ adapter.Rule) (net.Conn, adapter.Tracker) {
|
|
||||||
t := &Tracker{}
|
|
||||||
l, err := limiter.GetLimiter(m.Inbound)
|
l, err := limiter.GetLimiter(m.Inbound)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("get limiter for ", m.Inbound, " error: ", err)
|
log.Warn("get limiter for ", m.Inbound, " error: ", err)
|
||||||
return conn, t
|
return conn
|
||||||
}
|
|
||||||
if l.CheckDomainRule(m.Domain) {
|
|
||||||
conn.Close()
|
|
||||||
log.Error("[", m.Inbound, "] ",
|
|
||||||
"Limited ", m.User, " access to ", m.Domain, " by domain rule")
|
|
||||||
return conn, t
|
|
||||||
}
|
|
||||||
if l.CheckProtocolRule(m.Protocol) {
|
|
||||||
conn.Close()
|
|
||||||
log.Error("[", m.Inbound, "] ",
|
|
||||||
"Limited ", m.User, " use ", m.Domain, " by protocol rule")
|
|
||||||
return conn, t
|
|
||||||
}
|
}
|
||||||
ip := m.Source.Addr.String()
|
ip := m.Source.Addr.String()
|
||||||
if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, true); r {
|
if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, true, true); r {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn")
|
log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn")
|
||||||
return conn, t
|
return conn
|
||||||
} else if b != nil {
|
} else if b != nil {
|
||||||
conn = rate.NewConnRateLimiter(conn, b)
|
conn = rate.NewConnRateLimiter(conn, b)
|
||||||
}
|
}
|
||||||
t.AddLeave(func() {
|
|
||||||
l.ConnLimiter.DelConnCount(m.User, ip)
|
|
||||||
})
|
|
||||||
if h.EnableConnClear {
|
if h.EnableConnClear {
|
||||||
var key int
|
|
||||||
cc := &ConnClear{
|
cc := &ConnClear{
|
||||||
conns: map[int]io.Closer{
|
conns: map[int]io.Closer{
|
||||||
0: conn,
|
0: conn,
|
||||||
@@ -115,50 +87,32 @@ func (h *HookServer) RoutedConnection(_ context.Context, conn net.Conn, m adapte
|
|||||||
}
|
}
|
||||||
if v, ok := h.connClears.LoadOrStore(m.Inbound+m.User, cc); ok {
|
if v, ok := h.connClears.LoadOrStore(m.Inbound+m.User, cc); ok {
|
||||||
cc = v.(*ConnClear)
|
cc = v.(*ConnClear)
|
||||||
key = cc.AddConn(conn)
|
|
||||||
}
|
}
|
||||||
t.AddLeave(func() {
|
|
||||||
cc.DelConn(key)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if c, ok := h.counter.Load(m.Inbound); ok {
|
if c, ok := h.counter.Load(m.Inbound); ok {
|
||||||
return counter.NewConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(m.User)), t
|
return counter.NewConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(m.User))
|
||||||
} else {
|
} else {
|
||||||
c := counter.NewTrafficCounter()
|
c := counter.NewTrafficCounter()
|
||||||
h.counter.Store(m.Inbound, c)
|
h.counter.Store(m.Inbound, c)
|
||||||
return counter.NewConnCounter(conn, c.GetCounter(m.User)), t
|
return counter.NewConnCounter(conn, c.GetCounter(m.User))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HookServer) RoutedPacketConnection(_ context.Context, conn N.PacketConn, m adapter.InboundContext, _ adapter.Rule) (N.PacketConn, adapter.Tracker) {
|
func (h *HookServer) RoutedPacketConnection(_ context.Context, conn N.PacketConn, m adapter.InboundContext, _ adapter.Rule, _ adapter.Outbound) N.PacketConn {
|
||||||
t := &Tracker{}
|
|
||||||
l, err := limiter.GetLimiter(m.Inbound)
|
l, err := limiter.GetLimiter(m.Inbound)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("get limiter for ", m.Inbound, " error: ", err)
|
log.Warn("get limiter for ", m.Inbound, " error: ", err)
|
||||||
return conn, t
|
return conn
|
||||||
}
|
|
||||||
if l.CheckDomainRule(m.Domain) {
|
|
||||||
conn.Close()
|
|
||||||
log.Error("[", m.Inbound, "] ",
|
|
||||||
"Limited ", m.User, " access to ", m.Domain, " by domain rule")
|
|
||||||
return conn, t
|
|
||||||
}
|
|
||||||
if l.CheckProtocolRule(m.Protocol) {
|
|
||||||
conn.Close()
|
|
||||||
log.Error("[", m.Inbound, "] ",
|
|
||||||
"Limited ", m.User, " use ", m.Domain, " by protocol rule")
|
|
||||||
return conn, t
|
|
||||||
}
|
}
|
||||||
ip := m.Source.Addr.String()
|
ip := m.Source.Addr.String()
|
||||||
if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, true); r {
|
if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, false, false); r {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn")
|
log.Error("[", m.Inbound, "] ", "Limited ", m.User, " by ip or conn")
|
||||||
return conn, t
|
return conn
|
||||||
} else if b != nil {
|
} else if b != nil {
|
||||||
conn = rate.NewPacketConnCounter(conn, b)
|
//conn = rate.NewPacketConnCounter(conn, b)
|
||||||
}
|
}
|
||||||
if h.EnableConnClear {
|
if h.EnableConnClear {
|
||||||
var key int
|
|
||||||
cc := &ConnClear{
|
cc := &ConnClear{
|
||||||
conns: map[int]io.Closer{
|
conns: map[int]io.Closer{
|
||||||
0: conn,
|
0: conn,
|
||||||
@@ -166,57 +120,20 @@ func (h *HookServer) RoutedPacketConnection(_ context.Context, conn N.PacketConn
|
|||||||
}
|
}
|
||||||
if v, ok := h.connClears.LoadOrStore(m.Inbound+m.User, cc); ok {
|
if v, ok := h.connClears.LoadOrStore(m.Inbound+m.User, cc); ok {
|
||||||
cc = v.(*ConnClear)
|
cc = v.(*ConnClear)
|
||||||
key = cc.AddConn(conn)
|
|
||||||
}
|
}
|
||||||
t.AddLeave(func() {
|
|
||||||
cc.DelConn(key)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if c, ok := h.counter.Load(m.Inbound); ok {
|
if c, ok := h.counter.Load(m.Inbound); ok {
|
||||||
return counter.NewPacketConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(m.User)), t
|
return counter.NewPacketConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(m.User))
|
||||||
} else {
|
} else {
|
||||||
c := counter.NewTrafficCounter()
|
c := counter.NewTrafficCounter()
|
||||||
h.counter.Store(m.Inbound, c)
|
h.counter.Store(m.Inbound, c)
|
||||||
return counter.NewPacketConnCounter(conn, c.GetCounter(m.User)), t
|
return counter.NewPacketConnCounter(conn, c.GetCounter(m.User))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// not need
|
|
||||||
|
|
||||||
func (h *HookServer) Mode() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
func (h *HookServer) StoreSelected() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
func (h *HookServer) CacheFile() adapter.CacheFile {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (h *HookServer) HistoryStorage() *urltest.HistoryStorage {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HookServer) StoreFakeIP() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HookServer) ClearConn(inbound string, user string) {
|
func (h *HookServer) ClearConn(inbound string, user string) {
|
||||||
if v, ok := h.connClears.Load(inbound + user); ok {
|
if v, ok := h.connClears.Load(inbound + user); ok {
|
||||||
v.(*ConnClear).ClearConn()
|
v.(*ConnClear).ClearConn()
|
||||||
h.connClears.Delete(inbound + user)
|
h.connClears.Delete(inbound + user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tracker struct {
|
|
||||||
l []func()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tracker) AddLeave(f func()) {
|
|
||||||
t.l = append(t.l, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tracker) Leave() {
|
|
||||||
for i := range t.l {
|
|
||||||
t.l[i]()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import (
|
|||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
"github.com/sagernet/sing-box/inbound"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HttpNetworkConfig struct {
|
type HttpNetworkConfig struct {
|
||||||
@@ -40,6 +40,15 @@ type WsNetworkConfig struct {
|
|||||||
Headers map[string]string `json:"headers"`
|
Headers map[string]string `json:"headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GrpcNetworkConfig struct {
|
||||||
|
ServiceName string `json:"serviceName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HttpupgradeNetworkConfig struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
}
|
||||||
|
|
||||||
func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (option.Inbound, error) {
|
func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (option.Inbound, error) {
|
||||||
addr, err := netip.ParseAddr(c.ListenIP)
|
addr, err := netip.ParseAddr(c.ListenIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -50,16 +59,28 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
domainStrategy = c.SingOptions.DomainStrategy
|
domainStrategy = c.SingOptions.DomainStrategy
|
||||||
}
|
}
|
||||||
listen := option.ListenOptions{
|
listen := option.ListenOptions{
|
||||||
Listen: (*option.ListenAddress)(&addr),
|
Listen: (*badoption.Addr)(&addr),
|
||||||
ListenPort: uint16(info.Common.ServerPort),
|
ListenPort: uint16(info.Common.ServerPort),
|
||||||
ProxyProtocol: c.SingOptions.EnableProxyProtocol,
|
TCPFastOpen: c.SingOptions.TCPFastOpen,
|
||||||
TCPFastOpen: c.SingOptions.TCPFastOpen,
|
|
||||||
InboundOptions: option.InboundOptions{
|
InboundOptions: option.InboundOptions{
|
||||||
SniffEnabled: c.SingOptions.SniffEnabled,
|
SniffEnabled: c.SingOptions.SniffEnabled,
|
||||||
SniffOverrideDestination: c.SingOptions.SniffOverrideDestination,
|
SniffOverrideDestination: c.SingOptions.SniffOverrideDestination,
|
||||||
DomainStrategy: domainStrategy,
|
DomainStrategy: domainStrategy,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
var multiplex *option.InboundMultiplexOptions
|
||||||
|
if c.SingOptions.Multiplex != nil {
|
||||||
|
multiplexOption := option.InboundMultiplexOptions{
|
||||||
|
Enabled: c.SingOptions.Multiplex.Enabled,
|
||||||
|
Padding: c.SingOptions.Multiplex.Padding,
|
||||||
|
Brutal: &option.BrutalOptions{
|
||||||
|
Enabled: c.SingOptions.Multiplex.Brutal.Enabled,
|
||||||
|
UpMbps: c.SingOptions.Multiplex.Brutal.UpMbps,
|
||||||
|
DownMbps: c.SingOptions.Multiplex.Brutal.DownMbps,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
multiplex = &multiplexOption
|
||||||
|
}
|
||||||
var tls option.InboundTLSOptions
|
var tls option.InboundTLSOptions
|
||||||
switch info.Security {
|
switch info.Security {
|
||||||
case panel.Tls:
|
case panel.Tls:
|
||||||
@@ -98,7 +119,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
ServerPort: uint16(port),
|
ServerPort: uint16(port),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MaxTimeDifference: option.Duration(mtd),
|
MaxTimeDifference: badoption.Duration(mtd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in := option.Inbound{
|
in := option.Inbound{
|
||||||
@@ -141,7 +162,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
var (
|
var (
|
||||||
path string
|
path string
|
||||||
ed int
|
ed int
|
||||||
headers map[string]option.Listable[string]
|
headers map[string]badoption.Listable[string]
|
||||||
)
|
)
|
||||||
if len(n.NetworkSettings) != 0 {
|
if len(n.NetworkSettings) != 0 {
|
||||||
network := WsNetworkConfig{}
|
network := WsNetworkConfig{}
|
||||||
@@ -156,9 +177,9 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
}
|
}
|
||||||
path = u.Path
|
path = u.Path
|
||||||
ed, _ = strconv.Atoi(u.Query().Get("ed"))
|
ed, _ = strconv.Atoi(u.Query().Get("ed"))
|
||||||
headers = make(map[string]option.Listable[string], len(network.Headers))
|
headers = make(map[string]badoption.Listable[string], len(network.Headers))
|
||||||
for k, v := range network.Headers {
|
for k, v := range network.Headers {
|
||||||
headers[k] = option.Listable[string]{
|
headers[k] = badoption.Listable[string]{
|
||||||
v,
|
v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,30 +191,48 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
Headers: headers,
|
Headers: headers,
|
||||||
}
|
}
|
||||||
case "grpc":
|
case "grpc":
|
||||||
|
network := GrpcNetworkConfig{}
|
||||||
if len(n.NetworkSettings) != 0 {
|
if len(n.NetworkSettings) != 0 {
|
||||||
err := json.Unmarshal(n.NetworkSettings, &t.GRPCOptions)
|
err := json.Unmarshal(n.NetworkSettings, &network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
|
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
t.GRPCOptions = option.V2RayGRPCOptions{
|
||||||
|
ServiceName: network.ServiceName,
|
||||||
|
}
|
||||||
|
case "httpupgrade":
|
||||||
|
network := HttpupgradeNetworkConfig{}
|
||||||
|
if len(n.NetworkSettings) != 0 {
|
||||||
|
err := json.Unmarshal(n.NetworkSettings, &network)
|
||||||
|
if err != nil {
|
||||||
|
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.HTTPUpgradeOptions = option.V2RayHTTPUpgradeOptions{
|
||||||
|
Path: network.Path,
|
||||||
|
Host: network.Host,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if info.Type == "vless" {
|
if info.Type == "vless" {
|
||||||
in.Type = "vless"
|
in.Type = "vless"
|
||||||
in.VLESSOptions = option.VLESSInboundOptions{
|
in.Options = &option.VLESSInboundOptions{
|
||||||
ListenOptions: listen,
|
ListenOptions: listen,
|
||||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
TLS: &tls,
|
TLS: &tls,
|
||||||
},
|
},
|
||||||
Transport: &t,
|
Transport: &t,
|
||||||
|
Multiplex: multiplex,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
in.Type = "vmess"
|
in.Type = "vmess"
|
||||||
in.VMessOptions = option.VMessInboundOptions{
|
in.Options = &option.VMessInboundOptions{
|
||||||
ListenOptions: listen,
|
ListenOptions: listen,
|
||||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
TLS: &tls,
|
TLS: &tls,
|
||||||
},
|
},
|
||||||
Transport: &t,
|
Transport: &t,
|
||||||
|
Multiplex: multiplex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
@@ -203,25 +242,27 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
switch n.Cipher {
|
switch n.Cipher {
|
||||||
case "2022-blake3-aes-128-gcm":
|
case "2022-blake3-aes-128-gcm":
|
||||||
keyLength = 16
|
keyLength = 16
|
||||||
case "2022-blake3-aes-256-gcm":
|
case "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305":
|
||||||
keyLength = 32
|
keyLength = 32
|
||||||
default:
|
default:
|
||||||
keyLength = 16
|
keyLength = 16
|
||||||
}
|
}
|
||||||
in.ShadowsocksOptions = option.ShadowsocksInboundOptions{
|
ssoption := &option.ShadowsocksInboundOptions{
|
||||||
ListenOptions: listen,
|
ListenOptions: listen,
|
||||||
Method: n.Cipher,
|
Method: n.Cipher,
|
||||||
|
Multiplex: multiplex,
|
||||||
}
|
}
|
||||||
p := make([]byte, keyLength)
|
p := make([]byte, keyLength)
|
||||||
_, _ = rand.Read(p)
|
_, _ = rand.Read(p)
|
||||||
randomPasswd := string(p)
|
randomPasswd := string(p)
|
||||||
if strings.Contains(n.Cipher, "2022") {
|
if strings.Contains(n.Cipher, "2022") {
|
||||||
in.ShadowsocksOptions.Password = n.ServerKey
|
ssoption.Password = n.ServerKey
|
||||||
randomPasswd = base64.StdEncoding.EncodeToString([]byte(randomPasswd))
|
randomPasswd = base64.StdEncoding.EncodeToString([]byte(randomPasswd))
|
||||||
}
|
}
|
||||||
in.ShadowsocksOptions.Users = []option.ShadowsocksUser{{
|
ssoption.Users = []option.ShadowsocksUser{{
|
||||||
Password: randomPasswd,
|
Password: randomPasswd,
|
||||||
}}
|
}}
|
||||||
|
in.Options = ssoption
|
||||||
case "trojan":
|
case "trojan":
|
||||||
n := info.Trojan
|
n := info.Trojan
|
||||||
t := option.V2RayTransportOptions{
|
t := option.V2RayTransportOptions{
|
||||||
@@ -234,7 +275,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
var (
|
var (
|
||||||
path string
|
path string
|
||||||
ed int
|
ed int
|
||||||
headers map[string]option.Listable[string]
|
headers map[string]badoption.Listable[string]
|
||||||
)
|
)
|
||||||
if len(n.NetworkSettings) != 0 {
|
if len(n.NetworkSettings) != 0 {
|
||||||
network := WsNetworkConfig{}
|
network := WsNetworkConfig{}
|
||||||
@@ -249,9 +290,9 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
}
|
}
|
||||||
path = u.Path
|
path = u.Path
|
||||||
ed, _ = strconv.Atoi(u.Query().Get("ed"))
|
ed, _ = strconv.Atoi(u.Query().Get("ed"))
|
||||||
headers = make(map[string]option.Listable[string], len(network.Headers))
|
headers = make(map[string]badoption.Listable[string], len(network.Headers))
|
||||||
for k, v := range network.Headers {
|
for k, v := range network.Headers {
|
||||||
headers[k] = option.Listable[string]{
|
headers[k] = badoption.Listable[string]{
|
||||||
v,
|
v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,29 +304,34 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
Headers: headers,
|
Headers: headers,
|
||||||
}
|
}
|
||||||
case "grpc":
|
case "grpc":
|
||||||
|
network := GrpcNetworkConfig{}
|
||||||
if len(n.NetworkSettings) != 0 {
|
if len(n.NetworkSettings) != 0 {
|
||||||
err := json.Unmarshal(n.NetworkSettings, &t.GRPCOptions)
|
err := json.Unmarshal(n.NetworkSettings, &network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
|
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
t.GRPCOptions = option.V2RayGRPCOptions{
|
||||||
|
ServiceName: network.ServiceName,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
t.Type = ""
|
t.Type = ""
|
||||||
}
|
}
|
||||||
in.Type = "trojan"
|
in.Type = "trojan"
|
||||||
in.TrojanOptions = option.TrojanInboundOptions{
|
trojanoption := &option.TrojanInboundOptions{
|
||||||
ListenOptions: listen,
|
ListenOptions: listen,
|
||||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
TLS: &tls,
|
TLS: &tls,
|
||||||
},
|
},
|
||||||
Transport: &t,
|
Transport: &t,
|
||||||
|
Multiplex: multiplex,
|
||||||
}
|
}
|
||||||
if c.SingOptions.FallBackConfigs != nil {
|
if c.SingOptions.FallBackConfigs != nil {
|
||||||
// fallback handling
|
// fallback handling
|
||||||
fallback := c.SingOptions.FallBackConfigs.FallBack
|
fallback := c.SingOptions.FallBackConfigs.FallBack
|
||||||
fallbackPort, err := strconv.Atoi(fallback.ServerPort)
|
fallbackPort, err := strconv.Atoi(fallback.ServerPort)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
in.TrojanOptions.Fallback = &option.ServerOptions{
|
trojanoption.Fallback = &option.ServerOptions{
|
||||||
Server: fallback.Server,
|
Server: fallback.Server,
|
||||||
ServerPort: uint16(fallbackPort),
|
ServerPort: uint16(fallbackPort),
|
||||||
}
|
}
|
||||||
@@ -293,12 +339,13 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
fallbackForALPNMap := c.SingOptions.FallBackConfigs.FallBackForALPN
|
fallbackForALPNMap := c.SingOptions.FallBackConfigs.FallBackForALPN
|
||||||
fallbackForALPN := make(map[string]*option.ServerOptions, len(fallbackForALPNMap))
|
fallbackForALPN := make(map[string]*option.ServerOptions, len(fallbackForALPNMap))
|
||||||
if err := processFallback(c, fallbackForALPN); err == nil {
|
if err := processFallback(c, fallbackForALPN); err == nil {
|
||||||
in.TrojanOptions.FallbackForALPN = fallbackForALPN
|
trojanoption.FallbackForALPN = fallbackForALPN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
in.Options = trojanoption
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
in.Type = "hysteria"
|
in.Type = "hysteria"
|
||||||
in.HysteriaOptions = option.HysteriaInboundOptions{
|
in.Options = &option.HysteriaInboundOptions{
|
||||||
ListenOptions: listen,
|
ListenOptions: listen,
|
||||||
UpMbps: info.Hysteria.UpMbps,
|
UpMbps: info.Hysteria.UpMbps,
|
||||||
DownMbps: info.Hysteria.DownMbps,
|
DownMbps: info.Hysteria.DownMbps,
|
||||||
@@ -321,7 +368,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
Password: info.Hysteria2.ObfsType,
|
Password: info.Hysteria2.ObfsType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.Hysteria2Options = option.Hysteria2InboundOptions{
|
in.Options = &option.Hysteria2InboundOptions{
|
||||||
ListenOptions: listen,
|
ListenOptions: listen,
|
||||||
UpMbps: info.Hysteria2.UpMbps,
|
UpMbps: info.Hysteria2.UpMbps,
|
||||||
DownMbps: info.Hysteria2.DownMbps,
|
DownMbps: info.Hysteria2.DownMbps,
|
||||||
@@ -335,32 +382,20 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Sing) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error {
|
func (b *Sing) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error {
|
||||||
err := updateDNSConfig(info)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("build dns error: %s", err)
|
|
||||||
}
|
|
||||||
c, err := getInboundOptions(tag, info, config)
|
c, err := getInboundOptions(tag, info, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
in := b.box.Inbound()
|
||||||
in, err := inbound.New(
|
err = in.Create(
|
||||||
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,
|
tag,
|
||||||
c,
|
c.Type,
|
||||||
nil,
|
c.Options,
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("init inbound error: %s", err)
|
|
||||||
}
|
|
||||||
err = in.Start()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("start inbound error: %s", err)
|
|
||||||
}
|
|
||||||
b.inbounds[tag] = in
|
|
||||||
err = b.router.AddInbound(in)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("add inbound error: %s", err)
|
return fmt.Errorf("add inbound error: %s", err)
|
||||||
}
|
}
|
||||||
@@ -368,11 +403,8 @@ func (b *Sing) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Sing) DelNode(tag string) error {
|
func (b *Sing) DelNode(tag string) error {
|
||||||
err := b.inbounds[tag].Close()
|
in := b.box.Inbound()
|
||||||
if err != nil {
|
err := in.Remove(tag)
|
||||||
return fmt.Errorf("close inbound error: %s", err)
|
|
||||||
}
|
|
||||||
err = b.router.DelInbound(tag)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("delete inbound error: %s", err)
|
return fmt.Errorf("delete inbound error: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ package sing
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
vCore "github.com/InazumaV/V2bX/core"
|
vCore "github.com/InazumaV/V2bX/core"
|
||||||
"github.com/goccy/go-json"
|
|
||||||
box "github.com/sagernet/sing-box"
|
box "github.com/sagernet/sing-box"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ vCore.Core = (*Sing)(nil)
|
var _ vCore.Core = (*Sing)(nil)
|
||||||
@@ -37,13 +37,15 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func New(c *conf.CoreConfig) (vCore.Core, error) {
|
func New(c *conf.CoreConfig) (vCore.Core, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry())
|
||||||
options := option.Options{}
|
options := option.Options{}
|
||||||
if len(c.SingConfig.OriginalPath) != 0 {
|
if len(c.SingConfig.OriginalPath) != 0 {
|
||||||
data, err := os.ReadFile(c.SingConfig.OriginalPath)
|
data, err := os.ReadFile(c.SingConfig.OriginalPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read original config error: %s", err)
|
return nil, fmt.Errorf("read original config error: %s", err)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(data, &options)
|
options, err = json.UnmarshalExtendedContext[option.Options](ctx, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unmarshal original config error: %s", err)
|
return nil, fmt.Errorf("unmarshal original config error: %s", err)
|
||||||
}
|
}
|
||||||
@@ -63,45 +65,21 @@ func New(c *conf.CoreConfig) (vCore.Core, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
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: ctx,
|
||||||
Options: options,
|
Options: options,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hs := NewHookServer(c.SingConfig.EnableConnClear)
|
hs := NewHookServer(c.SingConfig.EnableConnClear)
|
||||||
b.Router().SetClashServer(hs)
|
b.Router().SetTracker(hs)
|
||||||
return &Sing{
|
return &Sing{
|
||||||
ctx: b.Router().GetCtx(),
|
ctx: b.Router().GetCtx(),
|
||||||
box: b,
|
box: b,
|
||||||
hookServer: hs,
|
hookServer: hs,
|
||||||
router: b.Router(),
|
router: b.Router(),
|
||||||
logFactory: b.LogFactory(),
|
logFactory: b.LogFactory(),
|
||||||
inbounds: make(map[string]adapter.Inbound),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,20 @@ import (
|
|||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
"github.com/InazumaV/V2bX/common/counter"
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
"github.com/InazumaV/V2bX/core"
|
"github.com/InazumaV/V2bX/core"
|
||||||
"github.com/sagernet/sing-box/inbound"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/protocol/hysteria"
|
||||||
|
"github.com/sagernet/sing-box/protocol/hysteria2"
|
||||||
|
"github.com/sagernet/sing-box/protocol/shadowsocks"
|
||||||
|
"github.com/sagernet/sing-box/protocol/trojan"
|
||||||
|
"github.com/sagernet/sing-box/protocol/vless"
|
||||||
|
"github.com/sagernet/sing-box/protocol/vmess"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
||||||
|
in, found := b.box.Inbound().Get(p.Tag)
|
||||||
|
if !found {
|
||||||
|
return 0, errors.New("the inbound not found")
|
||||||
|
}
|
||||||
switch p.NodeInfo.Type {
|
switch p.NodeInfo.Type {
|
||||||
case "vmess", "vless":
|
case "vmess", "vless":
|
||||||
if p.NodeInfo.Type == "vless" {
|
if p.NodeInfo.Type == "vless" {
|
||||||
@@ -23,7 +32,7 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
UUID: p.Users[i].Uuid,
|
UUID: p.Users[i].Uuid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = b.inbounds[p.Tag].(*inbound.VLESS).AddUsers(us)
|
err = in.(*vless.Inbound).AddUsers(us)
|
||||||
} else {
|
} else {
|
||||||
us := make([]option.VMessUser, len(p.Users))
|
us := make([]option.VMessUser, len(p.Users))
|
||||||
for i := range p.Users {
|
for i := range p.Users {
|
||||||
@@ -32,7 +41,7 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
UUID: p.Users[i].Uuid,
|
UUID: p.Users[i].Uuid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = b.inbounds[p.Tag].(*inbound.VMess).AddUsers(us)
|
err = in.(*vmess.Inbound).AddUsers(us)
|
||||||
}
|
}
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
us := make([]option.ShadowsocksUser, len(p.Users))
|
us := make([]option.ShadowsocksUser, len(p.Users))
|
||||||
@@ -49,7 +58,7 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
Password: password,
|
Password: password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = b.inbounds[p.Tag].(*inbound.ShadowsocksMulti).AddUsers(us)
|
err = in.(*shadowsocks.MultiInbound).AddUsers(us)
|
||||||
case "trojan":
|
case "trojan":
|
||||||
us := make([]option.TrojanUser, len(p.Users))
|
us := make([]option.TrojanUser, len(p.Users))
|
||||||
for i := range p.Users {
|
for i := range p.Users {
|
||||||
@@ -58,7 +67,7 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
Password: p.Users[i].Uuid,
|
Password: p.Users[i].Uuid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = b.inbounds[p.Tag].(*inbound.Trojan).AddUsers(us)
|
err = in.(*trojan.Inbound).AddUsers(us)
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
us := make([]option.HysteriaUser, len(p.Users))
|
us := make([]option.HysteriaUser, len(p.Users))
|
||||||
for i := range p.Users {
|
for i := range p.Users {
|
||||||
@@ -67,7 +76,7 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
AuthString: p.Users[i].Uuid,
|
AuthString: p.Users[i].Uuid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = b.inbounds[p.Tag].(*inbound.Hysteria).AddUsers(us)
|
err = in.(*hysteria.Inbound).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))
|
id := make([]int, len(p.Users))
|
||||||
@@ -78,7 +87,7 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
}
|
}
|
||||||
id[i] = p.Users[i].Id
|
id[i] = p.Users[i].Id
|
||||||
}
|
}
|
||||||
err = b.inbounds[p.Tag].(*inbound.Hysteria2).AddUsers(us, id)
|
err = in.(*hysteria2.Inbound).AddUsers(us, id)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -108,17 +117,17 @@ func (b *Sing) DelUsers(users []panel.UserInfo, tag string) error {
|
|||||||
if i, ok := b.inbounds[tag]; ok {
|
if i, ok := b.inbounds[tag]; ok {
|
||||||
switch i.Type() {
|
switch i.Type() {
|
||||||
case "vmess":
|
case "vmess":
|
||||||
del = i.(*inbound.VMess)
|
del = i.(*vmess.Inbound)
|
||||||
case "vless":
|
case "vless":
|
||||||
del = i.(*inbound.VLESS)
|
del = i.(*vless.Inbound)
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
del = i.(*inbound.ShadowsocksMulti)
|
del = i.(*shadowsocks.MultiInbound)
|
||||||
case "trojan":
|
case "trojan":
|
||||||
del = i.(*inbound.Trojan)
|
del = i.(*trojan.Inbound)
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
del = i.(*inbound.Hysteria)
|
del = i.(*hysteria.Inbound)
|
||||||
case "hysteria2":
|
case "hysteria2":
|
||||||
del = i.(*inbound.Hysteria2)
|
del = i.(*hysteria2.Inbound)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("the inbound not found")
|
return errors.New("the inbound not found")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.31.0
|
// protoc-gen-go v1.35.1
|
||||||
// protoc v4.23.4
|
// protoc v3.21.12
|
||||||
// source: config.proto
|
// source: config.proto
|
||||||
|
|
||||||
package dispatcher
|
package dispatcher
|
||||||
@@ -28,11 +28,9 @@ type SessionConfig struct {
|
|||||||
|
|
||||||
func (x *SessionConfig) Reset() {
|
func (x *SessionConfig) Reset() {
|
||||||
*x = SessionConfig{}
|
*x = SessionConfig{}
|
||||||
if protoimpl.UnsafeEnabled {
|
mi := &file_config_proto_msgTypes[0]
|
||||||
mi := &file_config_proto_msgTypes[0]
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms.StoreMessageInfo(mi)
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SessionConfig) String() string {
|
func (x *SessionConfig) String() string {
|
||||||
@@ -43,7 +41,7 @@ func (*SessionConfig) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *SessionConfig) ProtoReflect() protoreflect.Message {
|
func (x *SessionConfig) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_config_proto_msgTypes[0]
|
mi := &file_config_proto_msgTypes[0]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -68,11 +66,9 @@ type Config struct {
|
|||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
*x = Config{}
|
*x = Config{}
|
||||||
if protoimpl.UnsafeEnabled {
|
mi := &file_config_proto_msgTypes[1]
|
||||||
mi := &file_config_proto_msgTypes[1]
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms.StoreMessageInfo(mi)
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) String() string {
|
func (x *Config) String() string {
|
||||||
@@ -83,7 +79,7 @@ func (*Config) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_config_proto_msgTypes[1]
|
mi := &file_config_proto_msgTypes[1]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -140,7 +136,7 @@ func file_config_proto_rawDescGZIP() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
var file_config_proto_goTypes = []interface{}{
|
var file_config_proto_goTypes = []any{
|
||||||
(*SessionConfig)(nil), // 0: v2bx.core.app.dispatcher.SessionConfig
|
(*SessionConfig)(nil), // 0: v2bx.core.app.dispatcher.SessionConfig
|
||||||
(*Config)(nil), // 1: v2bx.core.app.dispatcher.Config
|
(*Config)(nil), // 1: v2bx.core.app.dispatcher.Config
|
||||||
}
|
}
|
||||||
@@ -158,32 +154,6 @@ func file_config_proto_init() {
|
|||||||
if File_config_proto != nil {
|
if File_config_proto != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*SessionConfig); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Config); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -165,24 +167,25 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network) (*
|
|||||||
if user != nil && len(user.Email) > 0 {
|
if user != nil && len(user.Email) > 0 {
|
||||||
limit, err = limiter.GetLimiter(sessionInbound.Tag)
|
limit, err = limiter.GetLimiter(sessionInbound.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("get limiter ", sessionInbound.Tag, " error: ", err).AtError().WriteToLog()
|
errors.LogInfo(ctx, "get limiter ", sessionInbound.Tag, " error: ", err)
|
||||||
common.Close(outboundLink.Writer)
|
common.Close(outboundLink.Writer)
|
||||||
common.Close(inboundLink.Writer)
|
common.Close(inboundLink.Writer)
|
||||||
common.Interrupt(outboundLink.Reader)
|
common.Interrupt(outboundLink.Reader)
|
||||||
common.Interrupt(inboundLink.Reader)
|
common.Interrupt(inboundLink.Reader)
|
||||||
return nil, nil, nil, newError("get limiter ", sessionInbound.Tag, " error: ", err)
|
return nil, nil, nil, errors.New("get limiter ", sessionInbound.Tag, " error: ", err)
|
||||||
}
|
}
|
||||||
// Speed Limit and Device Limit
|
// Speed Limit and Device Limit
|
||||||
w, reject := limit.CheckLimit(user.Email,
|
w, reject := limit.CheckLimit(user.Email,
|
||||||
sessionInbound.Source.Address.IP().String(),
|
sessionInbound.Source.Address.IP().String(),
|
||||||
network == net.Network_TCP)
|
network == net.Network_TCP,
|
||||||
|
sessionInbound.Source.Network == net.Network_TCP)
|
||||||
if reject {
|
if reject {
|
||||||
newError("Limited ", user.Email, " by conn or ip").AtWarning().WriteToLog()
|
errors.LogInfo(ctx, "Limited ", user.Email, " by conn or ip")
|
||||||
common.Close(outboundLink.Writer)
|
common.Close(outboundLink.Writer)
|
||||||
common.Close(inboundLink.Writer)
|
common.Close(inboundLink.Writer)
|
||||||
common.Interrupt(outboundLink.Reader)
|
common.Interrupt(outboundLink.Reader)
|
||||||
common.Interrupt(inboundLink.Reader)
|
common.Interrupt(inboundLink.Reader)
|
||||||
return nil, nil, nil, newError("Limited ", user.Email, " by conn or ip")
|
return nil, nil, nil, errors.New("Limited ", user.Email, " by conn or ip")
|
||||||
}
|
}
|
||||||
if w != nil {
|
if w != nil {
|
||||||
inboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w)
|
inboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w)
|
||||||
@@ -218,8 +221,20 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, d := range request.ExcludeForDomain {
|
for _, d := range request.ExcludeForDomain {
|
||||||
if strings.ToLower(domain) == d {
|
if strings.HasPrefix(d, "regexp:") {
|
||||||
return false
|
pattern := d[7:]
|
||||||
|
re, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfo(ctx, "Unable to compile regex")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if re.MatchString(domain) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if strings.ToLower(domain) == d {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protocolString := result.Protocol()
|
protocolString := result.Protocol()
|
||||||
@@ -227,12 +242,12 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
|
|||||||
protocolString = resComp.ProtocolForDomainResult()
|
protocolString = resComp.ProtocolForDomainResult()
|
||||||
}
|
}
|
||||||
for _, p := range request.OverrideDestinationForProtocol {
|
for _, p := range request.OverrideDestinationForProtocol {
|
||||||
if strings.HasPrefix(protocolString, p) || strings.HasPrefix(protocolString, p) {
|
if strings.HasPrefix(protocolString, p) || strings.HasPrefix(p, protocolString) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
||||||
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
|
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
|
||||||
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "Using sniffer ", protocolString, " since the fake DNS missed")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
|
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
|
||||||
@@ -282,7 +297,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
}
|
}
|
||||||
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||||
domain := result.Domain()
|
domain := result.Domain()
|
||||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "sniffed domain: ", domain)
|
||||||
destination.Address = net.ParseAddress(domain)
|
destination.Address = net.ParseAddress(domain)
|
||||||
protocol := result.Protocol()
|
protocol := result.Protocol()
|
||||||
if resComp, ok := result.(SnifferResultComposite); ok {
|
if resComp, ok := result.(SnifferResultComposite); ok {
|
||||||
@@ -307,7 +322,7 @@ 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.")
|
||||||
}
|
}
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if len(outbounds) == 0 {
|
if len(outbounds) == 0 {
|
||||||
@@ -336,7 +351,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||||||
}
|
}
|
||||||
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||||
domain := result.Domain()
|
domain := result.Domain()
|
||||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "sniffed domain: ", domain)
|
||||||
destination.Address = net.ParseAddress(domain)
|
destination.Address = net.ParseAddress(domain)
|
||||||
protocol := result.Protocol()
|
protocol := result.Protocol()
|
||||||
if resComp, ok := result.(SnifferResultComposite); ok {
|
if resComp, ok := result.(SnifferResultComposite); ok {
|
||||||
@@ -434,7 +449,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
var err error
|
var err error
|
||||||
l, err = limiter.GetLimiter(sessionInbound.Tag)
|
l, err = limiter.GetLimiter(sessionInbound.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("get limiter ", sessionInbound.Tag, " error: ", err).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
errors.LogError(ctx, "get limiter ", sessionInbound.Tag, " error: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if l != nil {
|
if l != nil {
|
||||||
@@ -445,20 +460,20 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
destStr = destination.Address.IP().String()
|
destStr = destination.Address.IP().String()
|
||||||
}
|
}
|
||||||
if l.CheckDomainRule(destStr) {
|
if l.CheckDomainRule(destStr) {
|
||||||
newError(fmt.Sprintf(
|
errors.LogError(ctx, fmt.Sprintf(
|
||||||
"User %s access domain %s reject by rule",
|
"User %s access domain %s reject by rule",
|
||||||
sessionInbound.User.Email,
|
sessionInbound.User.Email,
|
||||||
destStr)).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
destStr))
|
||||||
common.Close(link.Writer)
|
common.Close(link.Writer)
|
||||||
common.Interrupt(link.Reader)
|
common.Interrupt(link.Reader)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(protocol) != 0 {
|
if len(protocol) != 0 {
|
||||||
if l.CheckProtocolRule(protocol) {
|
if l.CheckProtocolRule(protocol) {
|
||||||
newError(fmt.Sprintf(
|
errors.LogError(ctx, fmt.Sprintf(
|
||||||
"User %s access protocol %s reject by rule",
|
"User %s access protocol %s reject by rule",
|
||||||
sessionInbound.User.Email,
|
sessionInbound.User.Email,
|
||||||
protocol)).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
protocol))
|
||||||
common.Close(link.Writer)
|
common.Close(link.Writer)
|
||||||
common.Interrupt(link.Reader)
|
common.Interrupt(link.Reader)
|
||||||
return
|
return
|
||||||
@@ -476,10 +491,10 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
ctx = session.SetForcedOutboundTagToContext(ctx, "")
|
ctx = session.SetForcedOutboundTagToContext(ctx, "")
|
||||||
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
|
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
|
||||||
isPickRoute = 1
|
isPickRoute = 1
|
||||||
newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]")
|
||||||
handler = h
|
handler = h
|
||||||
} else {
|
} else {
|
||||||
newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx))
|
errors.LogError(ctx, "non existing tag for platform initialized detour: ", forcedOutboundTag)
|
||||||
common.Close(link.Writer)
|
common.Close(link.Writer)
|
||||||
common.Interrupt(link.Reader)
|
common.Interrupt(link.Reader)
|
||||||
return
|
return
|
||||||
@@ -489,13 +504,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
outTag := route.GetOutboundTag()
|
outTag := route.GetOutboundTag()
|
||||||
if h := d.ohm.GetHandler(outTag); h != nil {
|
if h := d.ohm.GetHandler(outTag); h != nil {
|
||||||
isPickRoute = 2
|
isPickRoute = 2
|
||||||
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
|
||||||
handler = h
|
handler = h
|
||||||
} else {
|
} else {
|
||||||
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
errors.LogWarning(ctx, "non existing outTag: ", outTag)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "default route for ", destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +523,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
newError("default outbound handler not exist").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "default outbound handler not exist")
|
||||||
common.Close(link.Writer)
|
common.Close(link.Writer)
|
||||||
common.Interrupt(link.Reader)
|
common.Interrupt(link.Reader)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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,7 +23,7 @@ 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) {
|
||||||
@@ -31,7 +32,7 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
|||||||
if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP {
|
if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP {
|
||||||
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address)
|
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address)
|
||||||
if domainFromFakeDNS != "" {
|
if domainFromFakeDNS != "" {
|
||||||
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", ob.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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,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"
|
||||||
@@ -34,7 +35,7 @@ type Sniffer struct {
|
|||||||
func NewSniffer(ctx context.Context) *Sniffer {
|
func NewSniffer(ctx context.Context) *Sniffer {
|
||||||
ret := &Sniffer{
|
ret := &Sniffer{
|
||||||
sniffer: []protocolSnifferWithMetadata{
|
sniffer: []protocolSnifferWithMetadata{
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b, ctx) }, false, net.Network_TCP},
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import (
|
|||||||
_ "github.com/xtls/xray-core/proxy/http"
|
_ "github.com/xtls/xray-core/proxy/http"
|
||||||
_ "github.com/xtls/xray-core/proxy/loopback"
|
_ "github.com/xtls/xray-core/proxy/loopback"
|
||||||
_ "github.com/xtls/xray-core/proxy/shadowsocks"
|
_ "github.com/xtls/xray-core/proxy/shadowsocks"
|
||||||
|
_ "github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||||
_ "github.com/xtls/xray-core/proxy/socks"
|
_ "github.com/xtls/xray-core/proxy/socks"
|
||||||
_ "github.com/xtls/xray-core/proxy/trojan"
|
_ "github.com/xtls/xray-core/proxy/trojan"
|
||||||
_ "github.com/xtls/xray-core/proxy/vless/inbound"
|
_ "github.com/xtls/xray-core/proxy/vless/inbound"
|
||||||
@@ -51,13 +52,10 @@ import (
|
|||||||
//_ "github.com/xtls/xray-core/proxy/wireguard"
|
//_ "github.com/xtls/xray-core/proxy/wireguard"
|
||||||
|
|
||||||
// Transports
|
// Transports
|
||||||
//_ "github.com/xtls/xray-core/transport/internet/domainsocket"
|
|
||||||
_ "github.com/xtls/xray-core/transport/internet/grpc"
|
_ "github.com/xtls/xray-core/transport/internet/grpc"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/http"
|
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
||||||
|
|
||||||
//_ "github.com/xtls/xray-core/transport/internet/kcp"
|
|
||||||
//_ "github.com/xtls/xray-core/transport/internet/quic"
|
|
||||||
_ "github.com/xtls/xray-core/transport/internet/reality"
|
_ "github.com/xtls/xray-core/transport/internet/reality"
|
||||||
|
_ "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/tcp"
|
_ "github.com/xtls/xray-core/transport/internet/tcp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/tls"
|
_ "github.com/xtls/xray-core/transport/internet/tls"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/udp"
|
_ "github.com/xtls/xray-core/transport/internet/udp"
|
||||||
@@ -70,5 +68,5 @@ import (
|
|||||||
_ "github.com/xtls/xray-core/transport/internet/headers/tls"
|
_ "github.com/xtls/xray-core/transport/internet/headers/tls"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/utp"
|
_ "github.com/xtls/xray-core/transport/internet/headers/utp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/wechat"
|
_ "github.com/xtls/xray-core/transport/internet/headers/wechat"
|
||||||
//_ "github.com/xtls/xray-core/transport/internet/headers/wireguard"
|
_ "github.com/xtls/xray-core/transport/internet/headers/wireguard"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -131,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
|
||||||
@@ -180,7 +190,7 @@ func buildV2ray(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreCon
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
t := coreConf.TransportProtocol(nodeInfo.VAllss.Network)
|
t := coreConf.TransportProtocol(v.Network)
|
||||||
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
||||||
switch v.Network {
|
switch v.Network {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
@@ -194,18 +204,29 @@ func buildV2ray(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreCon
|
|||||||
return fmt.Errorf("unmarshal ws settings error: %s", err)
|
return fmt.Errorf("unmarshal ws settings error: %s", err)
|
||||||
}
|
}
|
||||||
case "grpc":
|
case "grpc":
|
||||||
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.GRPCConfig)
|
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.GRPCSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unmarshal grpc settings error: %s", err)
|
return fmt.Errorf("unmarshal grpc settings error: %s", err)
|
||||||
}
|
}
|
||||||
|
case "httpupgrade":
|
||||||
|
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.HTTPUPGRADESettings)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshal httpupgrade settings error: %s", err)
|
||||||
|
}
|
||||||
|
case "splithttp", "xhttp":
|
||||||
|
err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.SplitHTTPSettings)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshal xhttp settings error: %s", err)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return errors.New("the network type is not vail")
|
return errors.New("the network type is not vail")
|
||||||
}
|
}
|
||||||
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)
|
||||||
@@ -223,8 +244,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.GRPCSettings)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,11 @@ func buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey
|
|||||||
keyLength = 16
|
keyLength = 16
|
||||||
case "2022-blake3-aes-256-gcm":
|
case "2022-blake3-aes-256-gcm":
|
||||||
keyLength = 32
|
keyLength = 32
|
||||||
|
case "2022-blake3-chacha20-poly1305":
|
||||||
|
keyLength = 32
|
||||||
}
|
}
|
||||||
ssAccount := &shadowsocks_2022.User{
|
ssAccount := &shadowsocks_2022.Account{
|
||||||
Key: base64.StdEncoding.EncodeToString([]byte(userInfo.Uuid[:keyLength])),
|
Key: base64.StdEncoding.EncodeToString([]byte(userInfo.Uuid[:keyLength])),
|
||||||
}
|
}
|
||||||
return &protocol.User{
|
return &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
|
|||||||
306
go.mod
306
go.mod
@@ -1,139 +1,161 @@
|
|||||||
module github.com/InazumaV/V2bX
|
module github.com/InazumaV/V2bX
|
||||||
|
|
||||||
go 1.22
|
go 1.23
|
||||||
|
|
||||||
toolchain go1.22.0
|
toolchain go1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/apernet/hysteria/core v1.3.5-0.20240511211632-a3c4cfa4b5b2
|
github.com/apernet/hysteria/core/v2 v2.6.0
|
||||||
github.com/apernet/hysteria/extras v0.0.0-20240518025014-15e58468a72c
|
github.com/apernet/hysteria/extras/v2 v2.6.0
|
||||||
|
github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7
|
||||||
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.19.3-0.20241028134924-480950181787
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/go-resty/resty/v2 v2.15.3
|
||||||
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.2-0.20240618221538-6fa16eae919d
|
||||||
github.com/juju/ratelimit v1.0.2
|
github.com/juju/ratelimit v1.0.2
|
||||||
github.com/sagernet/sing v0.5.0-alpha.10
|
github.com/sagernet/sing v0.6.0-beta.6
|
||||||
github.com/sagernet/sing-box v1.9.0
|
github.com/sagernet/sing-box v1.10.4
|
||||||
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.1
|
||||||
github.com/spf13/viper v1.15.0
|
github.com/spf13/viper v1.18.2
|
||||||
github.com/xtls/xray-core v1.8.14-0.20240610141749-ea02ae74edb7
|
github.com/xtls/xray-core v1.8.25-0.20241212121918-7463561856f7
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/crypto v0.24.0
|
golang.org/x/crypto v0.30.0
|
||||||
golang.org/x/sys v0.21.0
|
golang.org/x/sys v0.28.0
|
||||||
google.golang.org/protobuf v1.34.1
|
google.golang.org/protobuf v1.35.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
|
cloud.google.com/go/auth v0.9.3 // indirect
|
||||||
cloud.google.com/go/compute v1.25.1 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/compute/metadata v0.5.1 // 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.14.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.7.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.10.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/ajg/form v1.5.1 // 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.63.15 // indirect
|
||||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
github.com/aws/aws-sdk-go-v2/config v1.27.33 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/lightsail v1.40.6 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.43.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 // indirect
|
||||||
|
github.com/aws/smithy-go v1.20.4 // indirect
|
||||||
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect
|
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect
|
||||||
|
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/caddyserver/certmagic v0.20.0 // indirect
|
github.com/caddyserver/certmagic v0.20.0 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/civo/civogo v0.3.11 // indirect
|
github.com/civo/civogo v0.3.11 // indirect
|
||||||
github.com/cloudflare/circl v1.3.8 // indirect
|
github.com/cloudflare/circl v1.4.0 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.104.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.2-0.20180830191138-d8f796af33cc // 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/v3 v3.1.5 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // 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.12 // indirect
|
github.com/go-chi/chi/v5 v5.1.0 // indirect
|
||||||
|
github.com/go-chi/render v1.0.3 // 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.4 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.1.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.2.0 // indirect
|
github.com/gofrs/flock v0.12.1 // indirect
|
||||||
|
github.com/gofrs/uuid/v5 v5.3.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.4 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/btree v1.1.2 // indirect
|
|
||||||
github.com/google/go-cmp v0.6.0 // 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-20240528025155-186aa0362fba // 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.8 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.12.2 // indirect
|
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
github.com/gophercloud/gophercloud v1.14.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.2 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||||
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 // 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/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // 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/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.8 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/libdns/alidns v1.0.3 // indirect
|
github.com/libdns/alidns v1.0.3 // indirect
|
||||||
github.com/libdns/cloudflare v0.1.1 // indirect
|
github.com/libdns/cloudflare v0.1.1 // indirect
|
||||||
github.com/libdns/libdns v0.2.2 // indirect
|
github.com/libdns/libdns v0.2.2 // indirect
|
||||||
github.com/linode/linodego v1.17.2 // indirect
|
github.com/linode/linodego v1.40.0 // indirect
|
||||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
|
||||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
github.com/mdlayher/socket v0.4.1 // indirect
|
github.com/mdlayher/socket v0.4.1 // indirect
|
||||||
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa // indirect
|
||||||
github.com/mholt/acmez v1.2.0 // indirect
|
github.com/mholt/acmez v1.2.0 // indirect
|
||||||
github.com/miekg/dns v1.1.59 // indirect
|
github.com/miekg/dns v1.1.62 // indirect
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
@@ -141,114 +163,128 @@ 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.4.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.19.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
||||||
github.com/ooni/go-libtor v1.1.8 // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
github.com/oracle/oci-go-sdk/v65 v65.73.0 // 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.6.0 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
github.com/pires/go-proxyproto v0.8.0 // 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.1-0.20181226105442-5d4384ee4fb2 // 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.5.1 // 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.45.0 // indirect
|
github.com/quic-go/quic-go v0.46.0 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.6 // 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-20240428053021-e691de28565f // indirect
|
github.com/sagernet/cors v1.2.1 // indirect
|
||||||
|
github.com/sagernet/fswatch v0.1.1 // indirect
|
||||||
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||||
github.com/sagernet/nftables v0.3.0-beta.2 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/sagernet/quic-go v0.45.0-beta.2 // indirect
|
github.com/sagernet/quic-go v0.48.2-beta.1 // indirect
|
||||||
github.com/sagernet/sing-dns v0.3.0-beta.5 // indirect
|
github.com/sagernet/sing-dns v0.4.0-beta.1 // indirect
|
||||||
github.com/sagernet/sing-mux v0.2.0 // indirect
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 // indirect
|
||||||
github.com/sagernet/sing-quic v0.2.0-beta.9 // indirect
|
github.com/sagernet/sing-quic v0.4.0-alpha.4 // 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.2.0-alpha.2 // indirect
|
||||||
github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240612134350-d9e105f31ddc // indirect
|
github.com/sagernet/sing-tun v0.6.0-beta.2 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.1.8 // indirect
|
github.com/sagernet/sing-vmess v0.2.0-beta.1 // indirect
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect
|
github.com/sagernet/utls v1.6.7 // indirect
|
||||||
github.com/sagernet/utls v1.5.4 // indirect
|
github.com/sagernet/wireguard-go v0.0.1-beta.5 // 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/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect
|
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 // indirect
|
||||||
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // 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/afero v1.9.3 // indirect
|
github.com/sony/gobreaker v0.5.0 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1002 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 // indirect
|
||||||
github.com/transip/gotransip/v6 v6.20.0 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
|
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||||
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
|
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
|
||||||
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 // indirect
|
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 // indirect
|
||||||
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
|
github.com/ultradns/ultradns-go-sdk v1.7.0-20240913052650-970ca9a // indirect
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
|
github.com/vishvananda/netlink v1.3.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
github.com/volcengine/volc-sdk-golang v1.0.177 // indirect
|
||||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc // indirect
|
github.com/vultr/govultr/v3 v3.9.1 // indirect
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
github.com/wyx2685/sing-vmess v0.0.0-20241213093025-7de21a950272 // indirect
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
||||||
|
github.com/yandex-cloud/go-genproto v0.0.0-20240911120709-1fa0cb6f47c2 // indirect
|
||||||
|
github.com/yandex-cloud/go-sdk v0.0.0-20240911121212-e4e74d0d02f5 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.12.0 // 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/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/ratelimit v0.2.0 // indirect
|
go.uber.org/ratelimit v0.3.0 // indirect
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
golang.org/x/net v0.26.0 // indirect
|
golang.org/x/net v0.32.0 // indirect
|
||||||
golang.org/x/oauth2 v0.18.0 // indirect
|
golang.org/x/oauth2 v0.23.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.25.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.169.0 // indirect
|
google.golang.org/api v0.197.0 // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
|
google.golang.org/grpc v1.67.1 // indirect
|
||||||
google.golang.org/grpc v1.64.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect
|
gopkg.in/ns1/ns1-go.v2 v2.12.0 // 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-20231202080848-1f7806d17489 // indirect
|
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
//github.com/apernet/hysteria/core v1.3.5-0.20240201034858-bb99579bb92c => /root/hysteria/core
|
//replace github.com/sagernet/sing-box v1.10.4 => /root/sing-box_mod
|
||||||
replace github.com/sagernet/sing-box v1.9.0 => github.com/wyx2685/sing-box_mod v0.0.8
|
|
||||||
|
replace github.com/sagernet/sing-box v1.10.4 => github.com/wyx2685/sing-box_mod v0.0.0-20241213112838-c7faac61b3fe
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package limiter
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -20,13 +21,13 @@ var limiter map[string]*Limiter
|
|||||||
func Init() {
|
func Init() {
|
||||||
limiter = map[string]*Limiter{}
|
limiter = map[string]*Limiter{}
|
||||||
c := task.Periodic{
|
c := task.Periodic{
|
||||||
Interval: time.Minute * 2,
|
Interval: time.Minute * 3,
|
||||||
Execute: ClearOnlineIP,
|
Execute: ClearOnlineIP,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
log.WithField("Type", "Limiter").
|
log.WithField("Type", "Limiter").
|
||||||
Debug("ClearOnlineIP started")
|
Debug("ClearOnlineIP started")
|
||||||
time.Sleep(time.Minute * 2)
|
time.Sleep(time.Minute * 3)
|
||||||
_ = c.Start()
|
_ = c.Start()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -36,10 +37,12 @@ type Limiter struct {
|
|||||||
ProtocolRules []string
|
ProtocolRules []string
|
||||||
SpeedLimit int
|
SpeedLimit int
|
||||||
UserOnlineIP *sync.Map // Key: Name, value: {Key: Ip, value: Uid}
|
UserOnlineIP *sync.Map // Key: Name, value: {Key: Ip, value: Uid}
|
||||||
UUIDtoUID map[string]int // Key: UUID, value: UID
|
OldUserOnline *sync.Map // Key: Ip, value: Uid
|
||||||
|
UUIDtoUID map[string]int // Key: UUID, value: Uid
|
||||||
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
|
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
|
||||||
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
|
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
|
||||||
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
|
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
|
||||||
|
AliveList map[int]int // Key: Uid, value: alive_ip
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserLimitInfo struct {
|
type UserLimitInfo struct {
|
||||||
@@ -48,15 +51,18 @@ type UserLimitInfo struct {
|
|||||||
DeviceLimit int
|
DeviceLimit int
|
||||||
DynamicSpeedLimit int
|
DynamicSpeedLimit int
|
||||||
ExpireTime int64
|
ExpireTime int64
|
||||||
|
OverLimit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limiter {
|
func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo, aliveList map[int]int) *Limiter {
|
||||||
info := &Limiter{
|
info := &Limiter{
|
||||||
SpeedLimit: l.SpeedLimit,
|
SpeedLimit: l.SpeedLimit,
|
||||||
UserOnlineIP: new(sync.Map),
|
UserOnlineIP: new(sync.Map),
|
||||||
UserLimitInfo: new(sync.Map),
|
UserLimitInfo: new(sync.Map),
|
||||||
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
|
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
|
||||||
SpeedLimiter: new(sync.Map),
|
SpeedLimiter: new(sync.Map),
|
||||||
|
AliveList: aliveList,
|
||||||
|
OldUserOnline: new(sync.Map),
|
||||||
}
|
}
|
||||||
uuidmap := make(map[string]int)
|
uuidmap := make(map[string]int)
|
||||||
for i := range users {
|
for i := range users {
|
||||||
@@ -69,6 +75,7 @@ func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limite
|
|||||||
if users[i].DeviceLimit != 0 {
|
if users[i].DeviceLimit != 0 {
|
||||||
userLimit.DeviceLimit = users[i].DeviceLimit
|
userLimit.DeviceLimit = users[i].DeviceLimit
|
||||||
}
|
}
|
||||||
|
userLimit.OverLimit = false
|
||||||
info.UserLimitInfo.Store(format.UserTag(tag, users[i].Uuid), userLimit)
|
info.UserLimitInfo.Store(format.UserTag(tag, users[i].Uuid), userLimit)
|
||||||
}
|
}
|
||||||
info.UUIDtoUID = uuidmap
|
info.UUIDtoUID = uuidmap
|
||||||
@@ -97,7 +104,9 @@ func DeleteLimiter(tag string) {
|
|||||||
func (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, deleted []panel.UserInfo) {
|
func (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, deleted []panel.UserInfo) {
|
||||||
for i := range deleted {
|
for i := range deleted {
|
||||||
l.UserLimitInfo.Delete(format.UserTag(tag, deleted[i].Uuid))
|
l.UserLimitInfo.Delete(format.UserTag(tag, deleted[i].Uuid))
|
||||||
|
l.UserOnlineIP.Delete(format.UserTag(tag, deleted[i].Uuid))
|
||||||
delete(l.UUIDtoUID, deleted[i].Uuid)
|
delete(l.UUIDtoUID, deleted[i].Uuid)
|
||||||
|
delete(l.AliveList, deleted[i].Id)
|
||||||
}
|
}
|
||||||
for i := range added {
|
for i := range added {
|
||||||
userLimit := &UserLimitInfo{
|
userLimit := &UserLimitInfo{
|
||||||
@@ -110,6 +119,7 @@ func (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, deleted []panel
|
|||||||
if added[i].DeviceLimit != 0 {
|
if added[i].DeviceLimit != 0 {
|
||||||
userLimit.DeviceLimit = added[i].DeviceLimit
|
userLimit.DeviceLimit = added[i].DeviceLimit
|
||||||
}
|
}
|
||||||
|
userLimit.OverLimit = false
|
||||||
l.UserLimitInfo.Store(format.UserTag(tag, added[i].Uuid), userLimit)
|
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
|
||||||
}
|
}
|
||||||
@@ -126,7 +136,10 @@ func (l *Limiter) UpdateDynamicSpeedLimit(tag, uuid string, limit int, expire ti
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool) (Bucket *ratelimit.Bucket, Reject bool) {
|
func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool) (Bucket *ratelimit.Bucket, Reject bool) {
|
||||||
|
// check if ipv4 mapped ipv6
|
||||||
|
ip = strings.TrimPrefix(ip, "::ffff:")
|
||||||
|
|
||||||
// ip and conn limiter
|
// ip and conn limiter
|
||||||
if l.ConnLimiter.AddConnCount(taguuid, ip, isTcp) {
|
if l.ConnLimiter.AddConnCount(taguuid, ip, isTcp) {
|
||||||
return nil, true
|
return nil, true
|
||||||
@@ -152,23 +165,33 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool) (Bucket *rat
|
|||||||
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
|
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if noSSUDP {
|
||||||
// Store online user for device limit
|
// Store online user for device limit
|
||||||
ipMap := new(sync.Map)
|
ipMap := new(sync.Map)
|
||||||
ipMap.Store(ip, uid)
|
ipMap.Store(ip, uid)
|
||||||
// If any device is online
|
aliveIp := l.AliveList[uid]
|
||||||
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
|
// If any device is online
|
||||||
ipMap := v.(*sync.Map)
|
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
|
||||||
// If this is a new ip
|
ipMap := v.(*sync.Map)
|
||||||
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
|
// If this is a new ip
|
||||||
counter := 0
|
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
|
||||||
ipMap.Range(func(key, value interface{}) bool {
|
if deviceLimit > 0 {
|
||||||
counter++
|
if deviceLimit <= aliveIp {
|
||||||
return true
|
ipMap.Delete(ip)
|
||||||
})
|
return nil, true
|
||||||
if counter > deviceLimit && deviceLimit > 0 {
|
}
|
||||||
ipMap.Delete(ip)
|
}
|
||||||
return nil, true
|
}
|
||||||
|
} else if v, ok := l.OldUserOnline.Load(ip); ok{
|
||||||
|
if v.(int) == uid {
|
||||||
|
l.OldUserOnline.Delete(ip)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if deviceLimit > 0 {
|
||||||
|
if deviceLimit <= aliveIp {
|
||||||
|
l.UserOnlineIP.Delete(taguuid)
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,17 +212,17 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool) (Bucket *rat
|
|||||||
|
|
||||||
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
|
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
|
||||||
var onlineUser []panel.OnlineUser
|
var onlineUser []panel.OnlineUser
|
||||||
|
|
||||||
l.UserOnlineIP.Range(func(key, value interface{}) bool {
|
l.UserOnlineIP.Range(func(key, value interface{}) bool {
|
||||||
email := key.(string)
|
taguuid := key.(string)
|
||||||
ipMap := value.(*sync.Map)
|
ipMap := value.(*sync.Map)
|
||||||
ipMap.Range(func(key, value interface{}) bool {
|
ipMap.Range(func(key, value interface{}) bool {
|
||||||
uid := value.(int)
|
uid := value.(int)
|
||||||
ip := key.(string)
|
ip := key.(string)
|
||||||
|
l.OldUserOnline.Store(ip, uid)
|
||||||
onlineUser = append(onlineUser, panel.OnlineUser{UID: uid, IP: ip})
|
onlineUser = append(onlineUser, panel.OnlineUser{UID: uid, IP: ip})
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
l.UserOnlineIP.Delete(email) // Reset online device
|
l.UserOnlineIP.Delete(taguuid) // Reset online device
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -210,23 +233,3 @@ type UserIpList struct {
|
|||||||
Uid int `json:"Uid"`
|
Uid int `json:"Uid"`
|
||||||
IpList []string `json:"Ips"`
|
IpList []string `json:"Ips"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func determineDeviceLimit(nodeLimit, userLimit int) (limit int) {
|
|
||||||
if nodeLimit == 0 || userLimit == 0 {
|
|
||||||
if nodeLimit > userLimit {
|
|
||||||
return nodeLimit
|
|
||||||
} else if nodeLimit < userLimit {
|
|
||||||
return userLimit
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if nodeLimit > userLimit {
|
|
||||||
return userLimit
|
|
||||||
} else if nodeLimit < userLimit {
|
|
||||||
return nodeLimit
|
|
||||||
} else {
|
|
||||||
return nodeLimit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -1,16 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"net/http"
|
|
||||||
//_ "net/http/pprof"
|
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/cmd"
|
"github.com/InazumaV/V2bX/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
//内存泄漏排查
|
|
||||||
//go func() {
|
|
||||||
// http.ListenAndServe("127.0.0.1:6060", nil)
|
|
||||||
//}()
|
|
||||||
cmd.Run()
|
cmd.Run()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type Controller struct {
|
|||||||
limiter *limiter.Limiter
|
limiter *limiter.Limiter
|
||||||
traffic map[string]int64
|
traffic map[string]int64
|
||||||
userList []panel.UserInfo
|
userList []panel.UserInfo
|
||||||
|
aliveMap map[int]int
|
||||||
info *panel.NodeInfo
|
info *panel.NodeInfo
|
||||||
nodeInfoMonitorPeriodic *task.Task
|
nodeInfoMonitorPeriodic *task.Task
|
||||||
userReportPeriodic *task.Task
|
userReportPeriodic *task.Task
|
||||||
@@ -54,6 +55,10 @@ func (c *Controller) Start() error {
|
|||||||
if len(c.userList) == 0 {
|
if len(c.userList) == 0 {
|
||||||
return errors.New("add users error: not have any user")
|
return errors.New("add users error: not have any user")
|
||||||
}
|
}
|
||||||
|
c.aliveMap, err = c.apiClient.GetUserAlive()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get user alive list: %s", err)
|
||||||
|
}
|
||||||
if len(c.Options.Name) == 0 {
|
if len(c.Options.Name) == 0 {
|
||||||
c.tag = c.buildNodeTag(node)
|
c.tag = c.buildNodeTag(node)
|
||||||
} else {
|
} else {
|
||||||
@@ -61,7 +66,7 @@ func (c *Controller) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add limiter
|
// add limiter
|
||||||
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList)
|
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, c.aliveMap)
|
||||||
// add rule limiter
|
// add rule limiter
|
||||||
if err = l.UpdateRule(&node.Rules); err != nil {
|
if err = l.UpdateRule(&node.Rules); err != nil {
|
||||||
return fmt.Errorf("update rule error: %s", err)
|
return fmt.Errorf("update rule error: %s", err)
|
||||||
|
|||||||
20
node/task.go
20
node/task.go
@@ -68,6 +68,15 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
}).Error("Get user list failed")
|
}).Error("Get user list failed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// get user alive
|
||||||
|
newA, err := c.apiClient.GetUserAlive()
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"tag": c.tag,
|
||||||
|
"err": err,
|
||||||
|
}).Error("Get alive list failed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if newN != nil {
|
if newN != nil {
|
||||||
c.info = newN
|
c.info = newN
|
||||||
// nodeInfo changed
|
// nodeInfo changed
|
||||||
@@ -92,9 +101,13 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
// Remove Old limiter
|
// Remove Old limiter
|
||||||
limiter.DeleteLimiter(c.tag)
|
limiter.DeleteLimiter(c.tag)
|
||||||
// Add new Limiter
|
// Add new Limiter
|
||||||
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList)
|
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, newA)
|
||||||
c.limiter = l
|
c.limiter = l
|
||||||
}
|
}
|
||||||
|
// update alive list
|
||||||
|
if newA != nil {
|
||||||
|
c.limiter.AliveList = newA
|
||||||
|
}
|
||||||
// Update rule
|
// Update rule
|
||||||
err = c.limiter.UpdateRule(&newN.Rules)
|
err = c.limiter.UpdateRule(&newN.Rules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -154,7 +167,10 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
// exit
|
// exit
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// update alive list
|
||||||
|
if newA != nil {
|
||||||
|
c.limiter.AliveList = newA
|
||||||
|
}
|
||||||
// node no changed, check users
|
// node no changed, check users
|
||||||
if len(newU) == 0 {
|
if len(newU) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
17
node/user.go
17
node/user.go
@@ -1,7 +1,6 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
@@ -15,11 +14,7 @@ func (c *Controller) reportUserTrafficTask() (err error) {
|
|||||||
up, down := c.server.GetUserTraffic(c.tag, c.userList[i].Uuid, true)
|
up, down := c.server.GetUserTraffic(c.tag, c.userList[i].Uuid, true)
|
||||||
if up > 0 || down > 0 {
|
if up > 0 || down > 0 {
|
||||||
if c.LimitConfig.EnableDynamicSpeedLimit {
|
if c.LimitConfig.EnableDynamicSpeedLimit {
|
||||||
if _, ok := c.traffic[c.userList[i].Uuid]; ok {
|
c.traffic[c.userList[i].Uuid] += up + down
|
||||||
c.traffic[c.userList[i].Uuid] += up + down
|
|
||||||
} else {
|
|
||||||
c.traffic[c.userList[i].Uuid] = up + down
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
userTraffic = append(userTraffic, panel.UserTraffic{
|
userTraffic = append(userTraffic, panel.UserTraffic{
|
||||||
UID: (c.userList)[i].Id,
|
UID: (c.userList)[i].Id,
|
||||||
@@ -56,18 +51,12 @@ func (c *Controller) reportUserTrafficTask() (err error) {
|
|||||||
result = append(result, online)
|
result = append(result, online)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reportOnline := make(map[int]int)
|
|
||||||
data := make(map[int][]string)
|
data := make(map[int][]string)
|
||||||
for _, onlineuser := range result {
|
for _, onlineuser := range result {
|
||||||
// json structure: { UID1:["ip1","ip2"],UID2:["ip3","ip4"] }
|
// json structure: { UID1:["ip1","ip2"],UID2:["ip3","ip4"] }
|
||||||
data[onlineuser.UID] = append(data[onlineuser.UID], fmt.Sprintf("%s_%d", onlineuser.IP, c.info.Id))
|
data[onlineuser.UID] = append(data[onlineuser.UID], onlineuser.IP)
|
||||||
if _, ok := reportOnline[onlineuser.UID]; ok {
|
|
||||||
reportOnline[onlineuser.UID]++
|
|
||||||
} else {
|
|
||||||
reportOnline[onlineuser.UID] = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err = c.apiClient.ReportNodeOnlineUsers(&data, &reportOnline); err != nil {
|
if err = c.apiClient.ReportNodeOnlineUsers(&data); err != nil {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"tag": c.tag,
|
"tag": c.tag,
|
||||||
"err": err,
|
"err": err,
|
||||||
|
|||||||
Reference in New Issue
Block a user