mirror of
https://github.com/wyx2685/V2bX.git
synced 2026-02-04 12:40:11 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d52a8932d | ||
|
|
06441afa79 | ||
|
|
f7b588fb45 | ||
|
|
9be082ede6 | ||
|
|
dce3ec1079 | ||
|
|
2990999f7b | ||
|
|
fe003fcb19 | ||
|
|
ea26985d7c | ||
|
|
32437f5e48 | ||
|
|
39dfd8b6dd | ||
|
|
eb51d3e13c | ||
|
|
dadeb6304b | ||
|
|
4c7b9f5eb9 | ||
|
|
63d88843b6 | ||
|
|
8d225f811b | ||
|
|
f6b587b275 | ||
|
|
d9b3d24465 | ||
|
|
fc284b3b9f | ||
|
|
eb92c4912d | ||
|
|
a68378670f | ||
|
|
d200a3336e | ||
|
|
6a95d576f1 | ||
|
|
dbe529bd48 | ||
|
|
8254e49297 | ||
|
|
9e8ad2619a | ||
|
|
d5fff6c433 | ||
|
|
fe896a61a3 | ||
|
|
a63198c20b | ||
|
|
180fb14dd1 | ||
|
|
44db7512d7 | ||
|
|
95263fea99 | ||
|
|
0b155bbf89 | ||
|
|
1c8c17b067 | ||
|
|
61606646b9 | ||
|
|
e2904ad126 | ||
|
|
903aef1fb5 | ||
|
|
2c43704090 | ||
|
|
d71df3a0df | ||
|
|
96baa0a99c | ||
|
|
e502624fe4 | ||
|
|
484faaf0c3 | ||
|
|
ec5dcc3c8a | ||
|
|
2f1362067b | ||
|
|
c755e9800b |
47
.github/workflows/Publish Docker image.yml
vendored
47
.github/workflows/Publish Docker image.yml
vendored
@@ -1,4 +1,5 @@
|
|||||||
name: Publish Docker image
|
name: Publish Docker image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
@@ -6,6 +7,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- 'dev_new'
|
- 'dev_new'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
IMAGE_NAME: ${{ github.repository_owner }}/v2bx
|
IMAGE_NAME: ${{ github.repository_owner }}/v2bx
|
||||||
@@ -20,40 +22,51 @@ jobs:
|
|||||||
- linux/amd64
|
- linux/amd64
|
||||||
- linux/arm64
|
- linux/arm64
|
||||||
steps:
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push by digest
|
- name: Build and push by digest
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
||||||
|
|
||||||
- name: Export digest
|
- name: Export digest
|
||||||
run: |
|
run: |
|
||||||
mkdir -p /tmp/digests
|
mkdir -p /tmp/digests
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
echo "${digest#sha256:}" > "/tmp/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: digests
|
name: digests-${{ env.PLATFORM_PAIR }}
|
||||||
path: /tmp/digests/*
|
path: /tmp/digests/*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
@@ -64,31 +77,37 @@ jobs:
|
|||||||
- build
|
- build
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: digests
|
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
|
pattern: digests-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Create manifest list and push
|
- name: Create manifest list and push
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
run: |
|
||||||
ls -al
|
cd /tmp/digests
|
||||||
echo docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
tags=$(echo '${{ steps.meta.outputs.json }}' | jq -cr '.tags | map("-t " + .) | join(" ")')
|
||||||
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
images=$(printf "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s " $(find . -type f -exec cat {} \;))
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
echo "Creating manifest with tags: $tags"
|
||||||
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
echo "Using images: $images"
|
||||||
|
docker buildx imagetools create $tags $images
|
||||||
|
|
||||||
- name: Inspect image
|
- name: Inspect image
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
||||||
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -10,14 +10,14 @@ on:
|
|||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
- "go.sum"
|
- "go.sum"
|
||||||
- ".github/workflows/*.yml"
|
- ".github/workflows/release.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
paths:
|
paths:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
- "go.sum"
|
- "go.sum"
|
||||||
- ".github/workflows/*.yml"
|
- ".github/workflows/release.yml"
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
@@ -109,10 +109,11 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23.2'
|
go-version: '1.25.0'
|
||||||
|
|
||||||
- name: Get project dependencies
|
- name: Get project dependencies
|
||||||
run: go mod download
|
run: |
|
||||||
|
go mod download
|
||||||
- name: Get release version
|
- name: Get release version
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
run: |
|
run: |
|
||||||
@@ -125,13 +126,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 with_gvisor" -trimpath -ldflags "-X 'github.com/InazumaV/V2bX/cmd.version=$version' -s -w -buildid="
|
GOEXPERIMENT=jsonv2 go build -v -o build_assets/V2bX -tags "sing xray hysteria2 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 with_gvisor" -trimpath -ldflags "-X 'github.com/InazumaV/V2bX/cmd.version=$version' -s -w -buildid="
|
GOEXPERIMENT=jsonv2 GOMIPS=softfloat go build -v -o build_assets/V2bX_softfloat -tags "sing xray hysteria2 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: |
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "sing-box_mod"]
|
||||||
|
path = sing-box_mod
|
||||||
|
url = https://github.com/wyx2685/sing-box_mod.git
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Build go
|
# Build go
|
||||||
FROM golang:1.23.2-alpine AS builder
|
FROM golang:1.25.0-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
RUN go mod download
|
RUN GOEXPERIMENT=jsonv2 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 with_gvisor"
|
RUN GOEXPERIMENT=jsonv2 go build -v -o V2bX -tags "sing xray hysteria2 with_quic with_grpc with_utls with_wireguard with_acme with_gvisor"
|
||||||
|
|
||||||
# Release
|
# Release
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ wget -N https://raw.githubusercontent.com/wyx2685/V2bX-script/master/install.sh
|
|||||||
## 构建
|
## 构建
|
||||||
``` bash
|
``` bash
|
||||||
# 通过-tags选项指定要编译的内核, 可选 xray, sing, hysteria2
|
# 通过-tags选项指定要编译的内核, 可选 xray, sing, hysteria2
|
||||||
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="
|
go build -v -o ./V2bX -tags "xray sing hysteria2 with_quic with_grpc with_utls with_wireguard with_acme" -trimpath -ldflags "-s -w -buildid="
|
||||||
```
|
```
|
||||||
|
|
||||||
## 配置文件及详细使用教程
|
## 配置文件及详细使用教程
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Security type
|
// Security type
|
||||||
@@ -32,6 +32,8 @@ type NodeInfo struct {
|
|||||||
VAllss *VAllssNode
|
VAllss *VAllssNode
|
||||||
Shadowsocks *ShadowsocksNode
|
Shadowsocks *ShadowsocksNode
|
||||||
Trojan *TrojanNode
|
Trojan *TrojanNode
|
||||||
|
Tuic *TuicNode
|
||||||
|
AnyTls *AnyTlsNode
|
||||||
Hysteria *HysteriaNode
|
Hysteria *HysteriaNode
|
||||||
Hysteria2 *Hysteria2Node
|
Hysteria2 *Hysteria2Node
|
||||||
Common *CommonNode
|
Common *CommonNode
|
||||||
@@ -73,12 +75,13 @@ type VAllssNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TlsSettings struct {
|
type TlsSettings struct {
|
||||||
ServerName string `json:"server_name"`
|
ServerName string `json:"server_name"`
|
||||||
Dest string `json:"dest"`
|
Dest string `json:"dest"`
|
||||||
ServerPort string `json:"server_port"`
|
ServerPort string `json:"server_port"`
|
||||||
ShortId string `json:"short_id"`
|
ShortId string `json:"short_id"`
|
||||||
PrivateKey string `json:"private_key"`
|
PrivateKey string `json:"private_key"`
|
||||||
Xver uint64 `json:"xver,string"`
|
Mldsa65Seed string `json:"mldsa65Seed"`
|
||||||
|
Xver uint64 `json:"xver,string"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RealityConfig struct {
|
type RealityConfig struct {
|
||||||
@@ -100,6 +103,17 @@ type TrojanNode struct {
|
|||||||
NetworkSettings json.RawMessage `json:"networkSettings"`
|
NetworkSettings json.RawMessage `json:"networkSettings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TuicNode struct {
|
||||||
|
CommonNode
|
||||||
|
CongestionControl string `json:"congestion_control"`
|
||||||
|
ZeroRTTHandshake bool `json:"zero_rtt_handshake"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnyTlsNode struct {
|
||||||
|
CommonNode
|
||||||
|
PaddingScheme []string `json:"padding_scheme,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type HysteriaNode struct {
|
type HysteriaNode struct {
|
||||||
CommonNode
|
CommonNode
|
||||||
UpMbps int `json:"up_mbps"`
|
UpMbps int `json:"up_mbps"`
|
||||||
@@ -109,10 +123,11 @@ type HysteriaNode struct {
|
|||||||
|
|
||||||
type Hysteria2Node struct {
|
type Hysteria2Node struct {
|
||||||
CommonNode
|
CommonNode
|
||||||
UpMbps int `json:"up_mbps"`
|
Ignore_Client_Bandwidth bool `json:"ignore_client_bandwidth"`
|
||||||
DownMbps int `json:"down_mbps"`
|
UpMbps int `json:"up_mbps"`
|
||||||
ObfsType string `json:"obfs"`
|
DownMbps int `json:"down_mbps"`
|
||||||
ObfsPassword string `json:"obfs-password"`
|
ObfsType string `json:"obfs"`
|
||||||
|
ObfsPassword string `json:"obfs-password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RawDNS struct {
|
type RawDNS struct {
|
||||||
@@ -202,6 +217,24 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
|
|||||||
cm = &rsp.CommonNode
|
cm = &rsp.CommonNode
|
||||||
node.Trojan = rsp
|
node.Trojan = rsp
|
||||||
node.Security = Tls
|
node.Security = Tls
|
||||||
|
case "tuic":
|
||||||
|
rsp := &TuicNode{}
|
||||||
|
err = json.Unmarshal(r.Body(), rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode tuic params error: %s", err)
|
||||||
|
}
|
||||||
|
cm = &rsp.CommonNode
|
||||||
|
node.Tuic = rsp
|
||||||
|
node.Security = Tls
|
||||||
|
case "anytls":
|
||||||
|
rsp := &AnyTlsNode{}
|
||||||
|
err = json.Unmarshal(r.Body(), rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode anytls params error: %s", err)
|
||||||
|
}
|
||||||
|
cm = &rsp.CommonNode
|
||||||
|
node.AnyTls = rsp
|
||||||
|
node.Security = Tls
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
rsp := &HysteriaNode{}
|
rsp := &HysteriaNode{}
|
||||||
err = json.Unmarshal(r.Body(), rsp)
|
err = json.Unmarshal(r.Body(), rsp)
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ func New(c *conf.ApiConfig) (*Client, error) {
|
|||||||
"shadowsocks",
|
"shadowsocks",
|
||||||
"hysteria",
|
"hysteria",
|
||||||
"hysteria2",
|
"hysteria2",
|
||||||
|
"tuic",
|
||||||
|
"anytls",
|
||||||
"vless":
|
"vless":
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ package panel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
"encoding/json/jsontext"
|
||||||
|
"encoding/json/v2"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OnlineUser struct {
|
type OnlineUser struct {
|
||||||
@@ -12,15 +16,14 @@ type OnlineUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id" msgpack:"id"`
|
||||||
Uuid string `json:"uuid"`
|
Uuid string `json:"uuid" msgpack:"uuid"`
|
||||||
SpeedLimit int `json:"speed_limit"`
|
SpeedLimit int `json:"speed_limit" msgpack:"speed_limit"`
|
||||||
DeviceLimit int `json:"device_limit"`
|
DeviceLimit int `json:"device_limit" msgpack:"device_limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserListBody struct {
|
type UserListBody struct {
|
||||||
//Msg string `json:"msg"`
|
Users []UserInfo `json:"users" msgpack:"users"`
|
||||||
Users []UserInfo `json:"users"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AliveMap struct {
|
type AliveMap struct {
|
||||||
@@ -32,7 +35,8 @@ 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").
|
SetHeader("X-Response-Format", "msgpack").
|
||||||
|
SetDoNotParseResponse(true).
|
||||||
Get(path)
|
Get(path)
|
||||||
if r == nil || r.RawResponse == nil {
|
if r == nil || r.RawResponse == nil {
|
||||||
return nil, fmt.Errorf("received nil response or raw response")
|
return nil, fmt.Errorf("received nil response or raw response")
|
||||||
@@ -47,8 +51,40 @@ func (c *Client) GetUserList() ([]UserInfo, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
userlist := &UserListBody{}
|
userlist := &UserListBody{}
|
||||||
if err := json.Unmarshal(r.Body(), userlist); err != nil {
|
if strings.Contains(r.Header().Get("Content-Type"), "application/x-msgpack") {
|
||||||
return nil, fmt.Errorf("unmarshal user list error: %w", err)
|
decoder := msgpack.NewDecoder(r.RawResponse.Body)
|
||||||
|
if err := decoder.Decode(userlist); err != nil {
|
||||||
|
return nil, fmt.Errorf("decode user list error: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dec := jsontext.NewDecoder(r.RawResponse.Body)
|
||||||
|
for {
|
||||||
|
tok, err := dec.ReadToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode user list error: %w", err)
|
||||||
|
}
|
||||||
|
if tok.Kind() == '"' && tok.String() == "users" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tok, err := dec.ReadToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode user list error: %w", err)
|
||||||
|
}
|
||||||
|
if tok.Kind() != '[' {
|
||||||
|
return nil, fmt.Errorf(`decode user list error: expected "users" array`)
|
||||||
|
}
|
||||||
|
for dec.PeekKind() != ']' {
|
||||||
|
val, err := dec.ReadValue()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode user list error: read user object: %w", err)
|
||||||
|
}
|
||||||
|
var u UserInfo
|
||||||
|
if err := json.Unmarshal(val, &u); err != nil {
|
||||||
|
return nil, fmt.Errorf("decode user list error: unmarshal user error: %w", err)
|
||||||
|
}
|
||||||
|
userlist.Users = append(userlist.Users, u)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.userEtag = r.Header().Get("ETag")
|
c.userEtag = r.Header().Get("ETag")
|
||||||
return userlist.Users, nil
|
return userlist.Users, nil
|
||||||
@@ -63,10 +99,12 @@ func (c *Client) GetUserAlive() (map[int]int, error) {
|
|||||||
Get(path)
|
Get(path)
|
||||||
if err != nil || r.StatusCode() >= 399 {
|
if err != nil || r.StatusCode() >= 399 {
|
||||||
c.AliveMap.Alive = make(map[int]int)
|
c.AliveMap.Alive = make(map[int]int)
|
||||||
|
return c.AliveMap.Alive, nil
|
||||||
}
|
}
|
||||||
if r == nil || r.RawResponse == nil {
|
if r == nil || r.RawResponse == nil {
|
||||||
fmt.Printf("received nil response or raw response")
|
fmt.Printf("received nil response or raw response")
|
||||||
c.AliveMap.Alive = make(map[int]int)
|
c.AliveMap.Alive = make(map[int]int)
|
||||||
|
return c.AliveMap.Alive, nil
|
||||||
}
|
}
|
||||||
defer r.RawResponse.Body.Close()
|
defer r.RawResponse.Body.Close()
|
||||||
if err := json.Unmarshal(r.Body(), c.AliveMap); err != nil {
|
if err := json.Unmarshal(r.Body(), c.AliveMap); err != nil {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TrafficCounter struct {
|
type TrafficCounter struct {
|
||||||
counters sync.Map
|
Counters sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrafficStorage struct {
|
type TrafficStorage struct {
|
||||||
@@ -18,26 +18,26 @@ func NewTrafficCounter() *TrafficCounter {
|
|||||||
return &TrafficCounter{}
|
return &TrafficCounter{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) GetCounter(id string) *TrafficStorage {
|
func (c *TrafficCounter) GetCounter(uuid string) *TrafficStorage {
|
||||||
if cts, ok := c.counters.Load(id); ok {
|
if cts, ok := c.Counters.Load(uuid); ok {
|
||||||
return cts.(*TrafficStorage)
|
return cts.(*TrafficStorage)
|
||||||
}
|
}
|
||||||
newStorage := &TrafficStorage{}
|
newStorage := &TrafficStorage{}
|
||||||
if cts, loaded := c.counters.LoadOrStore(id, newStorage); loaded {
|
if cts, loaded := c.Counters.LoadOrStore(uuid, newStorage); loaded {
|
||||||
return cts.(*TrafficStorage)
|
return cts.(*TrafficStorage)
|
||||||
}
|
}
|
||||||
return newStorage
|
return newStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) GetUpCount(id string) int64 {
|
func (c *TrafficCounter) GetUpCount(uuid string) int64 {
|
||||||
if cts, ok := c.counters.Load(id); ok {
|
if cts, ok := c.Counters.Load(uuid); ok {
|
||||||
return cts.(*TrafficStorage).UpCounter.Load()
|
return cts.(*TrafficStorage).UpCounter.Load()
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) GetDownCount(id string) int64 {
|
func (c *TrafficCounter) GetDownCount(uuid string) int64 {
|
||||||
if cts, ok := c.counters.Load(id); ok {
|
if cts, ok := c.Counters.Load(uuid); ok {
|
||||||
return cts.(*TrafficStorage).DownCounter.Load()
|
return cts.(*TrafficStorage).DownCounter.Load()
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
@@ -45,30 +45,30 @@ func (c *TrafficCounter) GetDownCount(id string) int64 {
|
|||||||
|
|
||||||
func (c *TrafficCounter) Len() int {
|
func (c *TrafficCounter) Len() int {
|
||||||
length := 0
|
length := 0
|
||||||
c.counters.Range(func(_, _ interface{}) bool {
|
c.Counters.Range(func(_, _ interface{}) bool {
|
||||||
length++
|
length++
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
return length
|
return length
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Reset(id string) {
|
func (c *TrafficCounter) Reset(uuid string) {
|
||||||
if cts, ok := c.counters.Load(id); ok {
|
if cts, ok := c.Counters.Load(uuid); ok {
|
||||||
cts.(*TrafficStorage).UpCounter.Store(0)
|
cts.(*TrafficStorage).UpCounter.Store(0)
|
||||||
cts.(*TrafficStorage).DownCounter.Store(0)
|
cts.(*TrafficStorage).DownCounter.Store(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Delete(id string) {
|
func (c *TrafficCounter) Delete(uuid string) {
|
||||||
c.counters.Delete(id)
|
c.Counters.Delete(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Rx(id string, n int) {
|
func (c *TrafficCounter) Rx(uuid string, n int) {
|
||||||
cts := c.GetCounter(id)
|
cts := c.GetCounter(uuid)
|
||||||
cts.DownCounter.Add(int64(n))
|
cts.DownCounter.Add(int64(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrafficCounter) Tx(id string, n int) {
|
func (c *TrafficCounter) Tx(uuid string, n int) {
|
||||||
cts := c.GetCounter(id)
|
cts := c.GetCounter(uuid)
|
||||||
cts.UpCounter.Add(int64(n))
|
cts.UpCounter.Add(int64(n))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/InazumaV/V2bX/common/json5"
|
"github.com/InazumaV/V2bX/common/json5"
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
"encoding/json/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/common/json5"
|
"github.com/InazumaV/V2bX/common/json5"
|
||||||
"github.com/goccy/go-json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeConfig struct {
|
type NodeConfig struct {
|
||||||
@@ -109,6 +110,7 @@ type Options struct {
|
|||||||
ListenIP string `json:"ListenIP"`
|
ListenIP string `json:"ListenIP"`
|
||||||
SendIP string `json:"SendIP"`
|
SendIP string `json:"SendIP"`
|
||||||
DeviceOnlineMinTraffic int64 `json:"DeviceOnlineMinTraffic"`
|
DeviceOnlineMinTraffic int64 `json:"DeviceOnlineMinTraffic"`
|
||||||
|
ReportMinTraffic int64 `json:"ReportMinTraffic"`
|
||||||
LimitConfig LimitConfig `json:"LimitConfig"`
|
LimitConfig LimitConfig `json:"LimitConfig"`
|
||||||
RawOptions json.RawMessage `json:"RawOptions"`
|
RawOptions json.RawMessage `json:"RawOptions"`
|
||||||
XrayOptions *XrayOptions `json:"XrayOptions"`
|
XrayOptions *XrayOptions `json:"XrayOptions"`
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SingConfig struct {
|
type SingConfig struct {
|
||||||
LogConfig SingLogConfig `json:"Log"`
|
LogConfig SingLogConfig `json:"Log"`
|
||||||
NtpConfig SingNtpConfig `json:"NTP"`
|
NtpConfig SingNtpConfig `json:"NTP"`
|
||||||
EnableConnClear bool `json:"EnableConnClear"`
|
OriginalPath string `json:"OriginalPath"`
|
||||||
OriginalPath string `json:"OriginalPath"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SingLogConfig struct {
|
type SingLogConfig struct {
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import (
|
|||||||
"github.com/apernet/hysteria/extras/v2/masq"
|
"github.com/apernet/hysteria/extras/v2/masq"
|
||||||
"github.com/apernet/hysteria/extras/v2/obfs"
|
"github.com/apernet/hysteria/extras/v2/obfs"
|
||||||
"github.com/apernet/hysteria/extras/v2/outbounds"
|
"github.com/apernet/hysteria/extras/v2/outbounds"
|
||||||
|
"github.com/apernet/hysteria/extras/v2/sniff"
|
||||||
|
eUtils "github.com/apernet/hysteria/extras/v2/utils"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -161,6 +163,29 @@ func (n *Hysteria2node) getBandwidthConfig(info *panel.NodeInfo) *server.Bandwid
|
|||||||
return band
|
return band
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Hysteria2node) getRequestHook(c *serverConfig) (server.RequestHook, error) {
|
||||||
|
if c.Sniff.Enable {
|
||||||
|
s := &sniff.Sniffer{
|
||||||
|
Timeout: c.Sniff.Timeout,
|
||||||
|
RewriteDomain: c.Sniff.RewriteDomain,
|
||||||
|
}
|
||||||
|
if c.Sniff.TCPPorts != "" {
|
||||||
|
s.TCPPorts = eUtils.ParsePortUnion(c.Sniff.TCPPorts)
|
||||||
|
if s.TCPPorts == nil {
|
||||||
|
return nil, fmt.Errorf("sniff.tcpPorts: invalid port union")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.Sniff.UDPPorts != "" {
|
||||||
|
s.UDPPorts = eUtils.ParsePortUnion(c.Sniff.UDPPorts)
|
||||||
|
if s.UDPPorts == nil {
|
||||||
|
return nil, fmt.Errorf("sniff.udpPorts: invalid port union")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Hysteria2node) getOutboundConfig(c *serverConfig) (server.Outbound, error) {
|
func (n *Hysteria2node) getOutboundConfig(c *serverConfig) (server.Outbound, error) {
|
||||||
// Resolver, ACL, actual outbound are all implemented through the Outbound interface.
|
// Resolver, ACL, actual outbound are all implemented through the Outbound interface.
|
||||||
// Depending on the config, we build a chain like this:
|
// Depending on the config, we build a chain like this:
|
||||||
@@ -299,7 +324,7 @@ func (n *Hysteria2node) getMasqHandler(tlsconfig *server.TLSConfig, conn net.Pac
|
|||||||
req.Header.Set("X-Forwarded-For", clientIP)
|
req.Header.Set("X-Forwarded-For", clientIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.Masquerade.Proxy.RewriteHost {
|
if c.Masquerade.Proxy.RewriteHost {
|
||||||
req.Host = req.URL.Host
|
req.Host = req.URL.Host
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -368,6 +393,10 @@ func (n *Hysteria2node) getHyConfig(info *panel.NodeInfo, config *conf.Options,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
sniff, err := n.getRequestHook(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
Outbound, err := n.getOutboundConfig(c)
|
Outbound, err := n.getOutboundConfig(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -380,9 +409,10 @@ func (n *Hysteria2node) getHyConfig(info *panel.NodeInfo, config *conf.Options,
|
|||||||
TLSConfig: *tls,
|
TLSConfig: *tls,
|
||||||
QUICConfig: *quic,
|
QUICConfig: *quic,
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
|
RequestHook: sniff,
|
||||||
Outbound: Outbound,
|
Outbound: Outbound,
|
||||||
BandwidthConfig: *n.getBandwidthConfig(info),
|
BandwidthConfig: *n.getBandwidthConfig(info),
|
||||||
IgnoreClientBandwidth: c.IgnoreClientBandwidth,
|
IgnoreClientBandwidth: info.Hysteria2.Ignore_Client_Bandwidth,
|
||||||
DisableUDP: c.DisableUDP,
|
DisableUDP: c.DisableUDP,
|
||||||
UDPIdleTimeout: c.UDPIdleTimeout,
|
UDPIdleTimeout: c.UDPIdleTimeout,
|
||||||
EventLogger: n.EventLogger,
|
EventLogger: n.EventLogger,
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ import (
|
|||||||
var _ server.TrafficLogger = (*HookServer)(nil)
|
var _ server.TrafficLogger = (*HookServer)(nil)
|
||||||
|
|
||||||
type HookServer struct {
|
type HookServer struct {
|
||||||
Tag string
|
Tag string
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
Counter sync.Map
|
Counter sync.Map
|
||||||
|
ReportMinTrafficBytes int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HookServer) TraceStream(stream quic.Stream, stats *server.StreamStats) {
|
func (h *HookServer) TraceStream(stream quic.Stream, stats *server.StreamStats) {
|
||||||
|
|||||||
@@ -120,11 +120,11 @@ func (l *serverLogger) UDPError(addr net.Addr, uuid string, sessionId uint32, er
|
|||||||
func initLogger(logLevel string, logFormat string) (*zap.Logger, error) {
|
func initLogger(logLevel string, logFormat string) (*zap.Logger, error) {
|
||||||
level, ok := logLevelMap[strings.ToLower(logLevel)]
|
level, ok := logLevelMap[strings.ToLower(logLevel)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf(fmt.Sprintf("unsupported log level: %s\n", logLevel))
|
return nil, fmt.Errorf("unsupported log level: %s", logLevel)
|
||||||
}
|
}
|
||||||
enc, ok := logFormatMap[strings.ToLower(logFormat)]
|
enc, ok := logFormatMap[strings.ToLower(logFormat)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf(fmt.Sprintf("unsupported log format: %s\n", logFormat))
|
return nil, fmt.Errorf("unsupported log format: %s", logFormat)
|
||||||
}
|
}
|
||||||
c := zap.Config{
|
c := zap.Config{
|
||||||
Level: zap.NewAtomicLevelAt(level),
|
Level: zap.NewAtomicLevelAt(level),
|
||||||
@@ -137,7 +137,7 @@ func initLogger(logLevel string, logFormat string) (*zap.Logger, error) {
|
|||||||
}
|
}
|
||||||
logger, err := c.Build()
|
logger, err := c.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(fmt.Sprintf("failed to initialize logger: %s\n", err))
|
return nil, fmt.Errorf("failed to initialize logger: %s", err)
|
||||||
}
|
}
|
||||||
return logger, nil
|
return logger, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ 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,
|
logger: h.Logger,
|
||||||
|
ReportMinTrafficBytes: config.ReportMinTraffic * 1024,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ type serverConfig struct {
|
|||||||
QUIC serverConfigQUIC `mapstructure:"quic"`
|
QUIC serverConfigQUIC `mapstructure:"quic"`
|
||||||
Bandwidth serverConfigBandwidth `mapstructure:"bandwidth"`
|
Bandwidth serverConfigBandwidth `mapstructure:"bandwidth"`
|
||||||
IgnoreClientBandwidth bool `mapstructure:"ignoreClientBandwidth"`
|
IgnoreClientBandwidth bool `mapstructure:"ignoreClientBandwidth"`
|
||||||
|
SpeedTest bool `mapstructure:"speedTest"`
|
||||||
DisableUDP bool `mapstructure:"disableUDP"`
|
DisableUDP bool `mapstructure:"disableUDP"`
|
||||||
UDPIdleTimeout time.Duration `mapstructure:"udpIdleTimeout"`
|
UDPIdleTimeout time.Duration `mapstructure:"udpIdleTimeout"`
|
||||||
Auth serverConfigAuth `mapstructure:"auth"`
|
Auth serverConfigAuth `mapstructure:"auth"`
|
||||||
Resolver serverConfigResolver `mapstructure:"resolver"`
|
Resolver serverConfigResolver `mapstructure:"resolver"`
|
||||||
|
Sniff serverConfigSniff `mapstructure:"sniff"`
|
||||||
ACL serverConfigACL `mapstructure:"acl"`
|
ACL serverConfigACL `mapstructure:"acl"`
|
||||||
Outbounds []serverConfigOutboundEntry `mapstructure:"outbounds"`
|
Outbounds []serverConfigOutboundEntry `mapstructure:"outbounds"`
|
||||||
TrafficStats serverConfigTrafficStats `mapstructure:"trafficStats"`
|
TrafficStats serverConfigTrafficStats `mapstructure:"trafficStats"`
|
||||||
@@ -112,6 +114,14 @@ type serverConfigResolver struct {
|
|||||||
HTTPS serverConfigResolverHTTPS `mapstructure:"https"`
|
HTTPS serverConfigResolverHTTPS `mapstructure:"https"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverConfigSniff struct {
|
||||||
|
Enable bool `mapstructure:"enable"`
|
||||||
|
Timeout time.Duration `mapstructure:"timeout"`
|
||||||
|
RewriteDomain bool `mapstructure:"rewriteDomain"`
|
||||||
|
TCPPorts string `mapstructure:"tcpPorts"`
|
||||||
|
UDPPorts string `mapstructure:"udpPorts"`
|
||||||
|
}
|
||||||
|
|
||||||
type serverConfigACL struct {
|
type serverConfigACL struct {
|
||||||
File string `mapstructure:"file"`
|
File string `mapstructure:"file"`
|
||||||
Inline []string `mapstructure:"inline"`
|
Inline []string `mapstructure:"inline"`
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ var _ server.Authenticator = &V2bX{}
|
|||||||
|
|
||||||
type V2bX struct {
|
type V2bX struct {
|
||||||
usersMap map[string]int
|
usersMap map[string]int
|
||||||
mutex sync.Mutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *V2bX) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) {
|
func (v *V2bX) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) {
|
||||||
v.mutex.Lock()
|
v.mutex.RLock()
|
||||||
defer v.mutex.Unlock()
|
defer v.mutex.RUnlock()
|
||||||
if _, exists := v.usersMap[auth]; exists {
|
if _, exists := v.usersMap[auth]; exists {
|
||||||
return true, auth
|
return true, auth
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ func (h *Hysteria2) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
|
|||||||
return len(p.Users), nil
|
return len(p.Users), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) DelUsers(users []panel.UserInfo, tag string) error {
|
func (h *Hysteria2) DelUsers(users []panel.UserInfo, tag string, _ *panel.NodeInfo) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
@@ -56,15 +56,38 @@ func (h *Hysteria2) DelUsers(users []panel.UserInfo, tag string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) GetUserTraffic(tag string, uuid string, reset bool) (up int64, down int64) {
|
func (h *Hysteria2) GetUserTrafficSlice(tag string, reset bool) ([]panel.UserTraffic, error) {
|
||||||
if v, ok := h.Hy2nodes[tag].TrafficLogger.(*HookServer).Counter.Load(tag); ok {
|
trafficSlice := make([]panel.UserTraffic, 0)
|
||||||
c := v.(*counter.TrafficCounter)
|
h.Auth.mutex.RLock()
|
||||||
up = c.GetUpCount(uuid)
|
defer h.Auth.mutex.RUnlock()
|
||||||
down = c.GetDownCount(uuid)
|
if _, ok := h.Hy2nodes[tag]; !ok {
|
||||||
if reset {
|
return nil, nil
|
||||||
c.Reset(uuid)
|
|
||||||
}
|
|
||||||
return up, down
|
|
||||||
}
|
}
|
||||||
return 0, 0
|
hook := h.Hy2nodes[tag].TrafficLogger.(*HookServer)
|
||||||
|
if v, ok := hook.Counter.Load(tag); ok {
|
||||||
|
c := v.(*counter.TrafficCounter)
|
||||||
|
c.Counters.Range(func(key, value interface{}) bool {
|
||||||
|
uuid := key.(string)
|
||||||
|
traffic := value.(*counter.TrafficStorage)
|
||||||
|
up := traffic.UpCounter.Load()
|
||||||
|
down := traffic.DownCounter.Load()
|
||||||
|
if up+down > hook.ReportMinTrafficBytes {
|
||||||
|
if reset {
|
||||||
|
traffic.UpCounter.Store(0)
|
||||||
|
traffic.DownCounter.Store(0)
|
||||||
|
}
|
||||||
|
trafficSlice = append(trafficSlice, panel.UserTraffic{
|
||||||
|
UID: h.Auth.usersMap[uuid],
|
||||||
|
Upload: up,
|
||||||
|
Download: down,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if len(trafficSlice) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return trafficSlice, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ type Core interface {
|
|||||||
AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error
|
AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error
|
||||||
DelNode(tag string) error
|
DelNode(tag string) error
|
||||||
AddUsers(p *AddUsersParams) (added int, err error)
|
AddUsers(p *AddUsersParams) (added int, err error)
|
||||||
GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64)
|
GetUserTrafficSlice(tag string, reset bool) ([]panel.UserTraffic, error)
|
||||||
DelUsers(users []panel.UserInfo, tag string) error
|
DelUsers(users []panel.UserInfo, tag string, info *panel.NodeInfo) error
|
||||||
Protocols() []string
|
Protocols() []string
|
||||||
Type() string
|
Type() string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package core
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
)
|
)
|
||||||
@@ -127,20 +128,20 @@ func (s *Selector) AddUsers(p *AddUsersParams) (added int, err error) {
|
|||||||
return t.(Core).AddUsers(p)
|
return t.(Core).AddUsers(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
|
func (s *Selector) GetUserTrafficSlice(tag string, reset bool) ([]panel.UserTraffic, error) {
|
||||||
t, e := s.nodes.Load(tag)
|
t, e := s.nodes.Load(tag)
|
||||||
if !e {
|
if !e {
|
||||||
return 0, 0
|
return nil, errors.New("the node is not have")
|
||||||
}
|
}
|
||||||
return t.(Core).GetUserTraffic(tag, uuid, reset)
|
return t.(Core).GetUserTrafficSlice(tag, reset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) DelUsers(users []panel.UserInfo, tag string) error {
|
func (s *Selector) DelUsers(users []panel.UserInfo, tag string, info *panel.NodeInfo) error {
|
||||||
t, e := s.nodes.Load(tag)
|
t, e := s.nodes.Load(tag)
|
||||||
if !e {
|
if !e {
|
||||||
return errors.New("the node is not have")
|
return errors.New("the node is not have")
|
||||||
}
|
}
|
||||||
return t.(Core).DelUsers(users, tag)
|
return t.(Core).DelUsers(users, tag, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) Protocols() []string {
|
func (s *Selector) Protocols() []string {
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
package sing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"github.com/InazumaV/V2bX/api/panel"
|
|
||||||
"github.com/goccy/go-json"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func updateDNSConfig(node *panel.NodeInfo) (err error) {
|
|
||||||
dnsPath := os.Getenv("SING_DNS_PATH")
|
|
||||||
if len(node.RawDNS.DNSJson) != 0 {
|
|
||||||
var prettyJSON bytes.Buffer
|
|
||||||
if err := json.Indent(&prettyJSON, node.RawDNS.DNSJson, "", " "); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = saveDnsConfig(prettyJSON.Bytes(), dnsPath)
|
|
||||||
} else if len(node.RawDNS.DNSMap) != 0 {
|
|
||||||
dnsConfig := DNSConfig{
|
|
||||||
Servers: []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"tag": "default",
|
|
||||||
"address": "https://8.8.8.8/dns-query",
|
|
||||||
"detour": "direct",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for id, value := range node.RawDNS.DNSMap {
|
|
||||||
dnsConfig.Servers = append(dnsConfig.Servers,
|
|
||||||
map[string]interface{}{
|
|
||||||
"tag": id,
|
|
||||||
"address": value["address"],
|
|
||||||
"address_resolver": "default",
|
|
||||||
"detour": "direct",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
rule := map[string]interface{}{
|
|
||||||
"server": id,
|
|
||||||
"disable_cache": true,
|
|
||||||
}
|
|
||||||
for _, ruleType := range []string{"domain_suffix", "domain_keyword", "domain_regex", "geosite"} {
|
|
||||||
var domains []string
|
|
||||||
for _, v := range value["domains"].([]string) {
|
|
||||||
split := strings.SplitN(v, ":", 2)
|
|
||||||
prefix := strings.ToLower(split[0])
|
|
||||||
if prefix == ruleType || (prefix == "domain" && ruleType == "domain_suffix") {
|
|
||||||
if len(split) > 1 {
|
|
||||||
domains = append(domains, split[1])
|
|
||||||
}
|
|
||||||
if len(domains) > 0 {
|
|
||||||
rule[ruleType] = domains
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dnsConfig.Rules = append(dnsConfig.Rules, rule)
|
|
||||||
}
|
|
||||||
dnsConfigJSON, err := json.MarshalIndent(dnsConfig, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
log.WithField("err", err).Error("Error marshaling dnsConfig to JSON")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = saveDnsConfig(dnsConfigJSON, dnsPath)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveDnsConfig(dns []byte, dnsPath string) (err error) {
|
|
||||||
currentData, err := os.ReadFile(dnsPath)
|
|
||||||
if err != nil {
|
|
||||||
log.WithField("err", err).Error("Failed to read SING_DNS_PATH")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !bytes.Equal(currentData, dns) {
|
|
||||||
if err = os.Truncate(dnsPath, 0); err != nil {
|
|
||||||
log.WithField("err", err).Error("Failed to clear SING DNS PATH file")
|
|
||||||
}
|
|
||||||
if err = os.WriteFile(dnsPath, dns, 0644); err != nil {
|
|
||||||
log.WithField("err", err).Error("Failed to write DNS to SING DNS PATH file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package sing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -20,82 +20,59 @@ import (
|
|||||||
var _ adapter.ConnectionTracker = (*HookServer)(nil)
|
var _ adapter.ConnectionTracker = (*HookServer)(nil)
|
||||||
|
|
||||||
type HookServer struct {
|
type HookServer struct {
|
||||||
EnableConnClear bool
|
counter sync.Map //map[string]*counter.TrafficCounter
|
||||||
counter sync.Map
|
|
||||||
connClears sync.Map
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConnClear struct {
|
|
||||||
lock sync.RWMutex
|
|
||||||
conns map[int]io.Closer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConnClear) AddConn(cn io.Closer) (key int) {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
key = len(c.conns)
|
|
||||||
c.conns[key] = cn
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConnClear) DelConn(key int) {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
delete(c.conns, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConnClear) ClearConn() {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
for _, c := range c.conns {
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HookServer) ModeList() []string {
|
func (h *HookServer) ModeList() []string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHookServer(enableClear bool) *HookServer {
|
|
||||||
server := &HookServer{
|
|
||||||
EnableConnClear: enableClear,
|
|
||||||
counter: sync.Map{},
|
|
||||||
connClears: sync.Map{},
|
|
||||||
}
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HookServer) RoutedConnection(_ context.Context, conn net.Conn, m adapter.InboundContext, _ adapter.Rule, _ adapter.Outbound) net.Conn {
|
func (h *HookServer) RoutedConnection(_ context.Context, conn net.Conn, m adapter.InboundContext, _ adapter.Rule, _ adapter.Outbound) net.Conn {
|
||||||
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
|
return conn
|
||||||
}
|
}
|
||||||
|
taguuid := format.UserTag(m.Inbound, m.User)
|
||||||
ip := m.Source.Addr.String()
|
ip := m.Source.Addr.String()
|
||||||
if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, true, true); r {
|
if b, r := l.CheckLimit(taguuid, 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
|
return conn
|
||||||
} else if b != nil {
|
} else if b != nil {
|
||||||
conn = rate.NewConnRateLimiter(conn, b)
|
conn = rate.NewConnRateLimiter(conn, b)
|
||||||
}
|
}
|
||||||
if h.EnableConnClear {
|
if l != nil {
|
||||||
cc := &ConnClear{
|
destStr := m.Destination.AddrString()
|
||||||
conns: map[int]io.Closer{
|
protocol := m.Protocol
|
||||||
0: conn,
|
if l.CheckDomainRule(destStr) {
|
||||||
},
|
log.Error(fmt.Sprintf(
|
||||||
|
"User %s access domain %s reject by rule",
|
||||||
|
m.User,
|
||||||
|
destStr))
|
||||||
|
conn.Close()
|
||||||
|
return conn
|
||||||
}
|
}
|
||||||
if v, ok := h.connClears.LoadOrStore(m.Inbound+m.User, cc); ok {
|
if len(protocol) != 0 {
|
||||||
cc = v.(*ConnClear)
|
if l.CheckProtocolRule(protocol) {
|
||||||
|
log.Error(fmt.Sprintf(
|
||||||
|
"User %s access protocol %s reject by rule",
|
||||||
|
m.User,
|
||||||
|
protocol))
|
||||||
|
conn.Close()
|
||||||
|
return conn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c, ok := h.counter.Load(m.Inbound); ok {
|
var t *counter.TrafficCounter
|
||||||
return counter.NewConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(m.User))
|
if c, ok := h.counter.Load(m.Inbound); !ok {
|
||||||
|
t = counter.NewTrafficCounter()
|
||||||
|
h.counter.Store(m.Inbound, t)
|
||||||
} else {
|
} else {
|
||||||
c := counter.NewTrafficCounter()
|
t = c.(*counter.TrafficCounter)
|
||||||
h.counter.Store(m.Inbound, c)
|
|
||||||
return counter.NewConnCounter(conn, c.GetCounter(m.User))
|
|
||||||
}
|
}
|
||||||
|
conn = counter.NewConnCounter(conn, t.GetCounter(m.User))
|
||||||
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HookServer) RoutedPacketConnection(_ context.Context, conn N.PacketConn, m adapter.InboundContext, _ adapter.Rule, _ adapter.Outbound) N.PacketConn {
|
func (h *HookServer) RoutedPacketConnection(_ context.Context, conn N.PacketConn, m adapter.InboundContext, _ adapter.Rule, _ adapter.Outbound) N.PacketConn {
|
||||||
@@ -105,35 +82,43 @@ func (h *HookServer) RoutedPacketConnection(_ context.Context, conn N.PacketConn
|
|||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
ip := m.Source.Addr.String()
|
ip := m.Source.Addr.String()
|
||||||
if b, r := l.CheckLimit(format.UserTag(m.Inbound, m.User), ip, false, false); r {
|
taguuid := format.UserTag(m.Inbound, m.User)
|
||||||
|
if b, r := l.CheckLimit(taguuid, 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
|
return conn
|
||||||
} else if b != nil {
|
} else if b != nil {
|
||||||
//conn = rate.NewPacketConnCounter(conn, b)
|
//conn = rate.NewPacketConnCounter(conn, b)
|
||||||
}
|
}
|
||||||
if h.EnableConnClear {
|
if l != nil {
|
||||||
cc := &ConnClear{
|
destStr := m.Destination.AddrString()
|
||||||
conns: map[int]io.Closer{
|
protocol := m.Destination.Network()
|
||||||
0: conn,
|
if l.CheckDomainRule(destStr) {
|
||||||
},
|
log.Error(fmt.Sprintf(
|
||||||
|
"User %s access domain %s reject by rule",
|
||||||
|
m.User,
|
||||||
|
destStr))
|
||||||
|
conn.Close()
|
||||||
|
return conn
|
||||||
}
|
}
|
||||||
if v, ok := h.connClears.LoadOrStore(m.Inbound+m.User, cc); ok {
|
if len(protocol) != 0 {
|
||||||
cc = v.(*ConnClear)
|
if l.CheckProtocolRule(protocol) {
|
||||||
|
log.Error(fmt.Sprintf(
|
||||||
|
"User %s access protocol %s reject by rule",
|
||||||
|
m.User,
|
||||||
|
protocol))
|
||||||
|
conn.Close()
|
||||||
|
return conn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c, ok := h.counter.Load(m.Inbound); ok {
|
var t *counter.TrafficCounter
|
||||||
return counter.NewPacketConnCounter(conn, c.(*counter.TrafficCounter).GetCounter(m.User))
|
if c, ok := h.counter.Load(m.Inbound); !ok {
|
||||||
|
t = counter.NewTrafficCounter()
|
||||||
|
h.counter.Store(m.Inbound, t)
|
||||||
} else {
|
} else {
|
||||||
c := counter.NewTrafficCounter()
|
t = c.(*counter.TrafficCounter)
|
||||||
h.counter.Store(m.Inbound, c)
|
|
||||||
return counter.NewPacketConnCounter(conn, c.GetCounter(m.User))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HookServer) ClearConn(inbound string, user string) {
|
|
||||||
if v, ok := h.connClears.Load(inbound + user); ok {
|
|
||||||
v.(*ConnClear).ClearConn()
|
|
||||||
h.connClears.Delete(inbound + user)
|
|
||||||
}
|
}
|
||||||
|
conn = counter.NewPacketConnCounter(conn, t.GetCounter(m.User))
|
||||||
|
return conn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"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/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"
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
@@ -54,19 +55,10 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return option.Inbound{}, fmt.Errorf("the listen ip not vail")
|
return option.Inbound{}, fmt.Errorf("the listen ip not vail")
|
||||||
}
|
}
|
||||||
var domainStrategy option.DomainStrategy
|
|
||||||
if c.SingOptions.EnableDNS {
|
|
||||||
domainStrategy = c.SingOptions.DomainStrategy
|
|
||||||
}
|
|
||||||
listen := option.ListenOptions{
|
listen := option.ListenOptions{
|
||||||
Listen: (*badoption.Addr)(&addr),
|
Listen: (*badoption.Addr)(&addr),
|
||||||
ListenPort: uint16(info.Common.ServerPort),
|
ListenPort: uint16(info.Common.ServerPort),
|
||||||
TCPFastOpen: c.SingOptions.TCPFastOpen,
|
TCPFastOpen: c.SingOptions.TCPFastOpen,
|
||||||
InboundOptions: option.InboundOptions{
|
|
||||||
SniffEnabled: c.SingOptions.SniffEnabled,
|
|
||||||
SniffOverrideDestination: c.SingOptions.SniffOverrideDestination,
|
|
||||||
DomainStrategy: domainStrategy,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
var multiplex *option.InboundMultiplexOptions
|
var multiplex *option.InboundMultiplexOptions
|
||||||
if c.SingOptions.Multiplex != nil {
|
if c.SingOptions.Multiplex != nil {
|
||||||
@@ -343,6 +335,26 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.Options = trojanoption
|
in.Options = trojanoption
|
||||||
|
case "tuic":
|
||||||
|
in.Type = "tuic"
|
||||||
|
tls.ALPN = append(tls.ALPN, "h3")
|
||||||
|
in.Options = &option.TUICInboundOptions{
|
||||||
|
ListenOptions: listen,
|
||||||
|
CongestionControl: info.Tuic.CongestionControl,
|
||||||
|
ZeroRTTHandshake: info.Tuic.ZeroRTTHandshake,
|
||||||
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
|
TLS: &tls,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "anytls":
|
||||||
|
in.Type = "anytls"
|
||||||
|
in.Options = &option.AnyTLSInboundOptions{
|
||||||
|
ListenOptions: listen,
|
||||||
|
PaddingScheme: info.AnyTls.PaddingScheme,
|
||||||
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
|
TLS: &tls,
|
||||||
|
},
|
||||||
|
}
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
in.Type = "hysteria"
|
in.Type = "hysteria"
|
||||||
in.Options = &option.HysteriaInboundOptions{
|
in.Options = &option.HysteriaInboundOptions{
|
||||||
@@ -369,10 +381,11 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.Options = &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,
|
||||||
Obfs: obfs,
|
IgnoreClientBandwidth: info.Hysteria2.Ignore_Client_Bandwidth,
|
||||||
|
Obfs: obfs,
|
||||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
TLS: &tls,
|
TLS: &tls,
|
||||||
},
|
},
|
||||||
@@ -382,6 +395,7 @@ 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 {
|
||||||
|
b.nodeReportMinTrafficBytes[tag] = config.ReportMinTraffic * 1024
|
||||||
c, err := getInboundOptions(tag, info, config)
|
c, err := getInboundOptions(tag, info, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/include"
|
"github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
@@ -24,12 +25,18 @@ type DNSConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Sing struct {
|
type Sing struct {
|
||||||
box *box.Box
|
box *box.Box
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
hookServer *HookServer
|
hookServer *HookServer
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
logFactory log.Factory
|
logFactory log.Factory
|
||||||
inbounds map[string]adapter.Inbound
|
users *UserMap
|
||||||
|
nodeReportMinTrafficBytes map[string]int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserMap struct {
|
||||||
|
uidMap map[string]int
|
||||||
|
mapLock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -38,7 +45,7 @@ func init() {
|
|||||||
|
|
||||||
func New(c *conf.CoreConfig) (vCore.Core, error) {
|
func New(c *conf.CoreConfig) (vCore.Core, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry())
|
ctx = box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), include.DNSTransportRegistry(), include.ServiceRegistry())
|
||||||
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)
|
||||||
@@ -72,14 +79,20 @@ func New(c *conf.CoreConfig) (vCore.Core, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hs := NewHookServer(c.SingConfig.EnableConnClear)
|
hs := &HookServer{
|
||||||
b.Router().SetTracker(hs)
|
counter: sync.Map{},
|
||||||
|
}
|
||||||
|
b.Router().AppendTracker(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(),
|
||||||
|
users: &UserMap{
|
||||||
|
uidMap: make(map[string]int),
|
||||||
|
},
|
||||||
|
nodeReportMinTrafficBytes: make(map[string]int64),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +110,8 @@ func (b *Sing) Protocols() []string {
|
|||||||
"vless",
|
"vless",
|
||||||
"shadowsocks",
|
"shadowsocks",
|
||||||
"trojan",
|
"trojan",
|
||||||
|
"tuic",
|
||||||
|
"anytls",
|
||||||
"hysteria",
|
"hysteria",
|
||||||
"hysteria2",
|
"hysteria2",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import (
|
|||||||
"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/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/protocol/anytls"
|
||||||
"github.com/sagernet/sing-box/protocol/hysteria"
|
"github.com/sagernet/sing-box/protocol/hysteria"
|
||||||
"github.com/sagernet/sing-box/protocol/hysteria2"
|
"github.com/sagernet/sing-box/protocol/hysteria2"
|
||||||
"github.com/sagernet/sing-box/protocol/shadowsocks"
|
"github.com/sagernet/sing-box/protocol/shadowsocks"
|
||||||
"github.com/sagernet/sing-box/protocol/trojan"
|
"github.com/sagernet/sing-box/protocol/trojan"
|
||||||
|
"github.com/sagernet/sing-box/protocol/tuic"
|
||||||
"github.com/sagernet/sing-box/protocol/vless"
|
"github.com/sagernet/sing-box/protocol/vless"
|
||||||
"github.com/sagernet/sing-box/protocol/vmess"
|
"github.com/sagernet/sing-box/protocol/vmess"
|
||||||
)
|
)
|
||||||
@@ -21,28 +23,31 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
if !found {
|
if !found {
|
||||||
return 0, errors.New("the inbound not found")
|
return 0, errors.New("the inbound not found")
|
||||||
}
|
}
|
||||||
|
b.users.mapLock.Lock()
|
||||||
|
defer b.users.mapLock.Unlock()
|
||||||
|
for i := range p.Users {
|
||||||
|
b.users.uidMap[p.Users[i].Uuid] = p.Users[i].Id
|
||||||
|
}
|
||||||
switch p.NodeInfo.Type {
|
switch p.NodeInfo.Type {
|
||||||
case "vmess", "vless":
|
case "vless":
|
||||||
if p.NodeInfo.Type == "vless" {
|
us := make([]option.VLESSUser, len(p.Users))
|
||||||
us := make([]option.VLESSUser, len(p.Users))
|
for i := range p.Users {
|
||||||
for i := range p.Users {
|
us[i] = option.VLESSUser{
|
||||||
us[i] = option.VLESSUser{
|
Name: p.Users[i].Uuid,
|
||||||
Name: p.Users[i].Uuid,
|
Flow: p.VAllss.Flow,
|
||||||
Flow: p.VAllss.Flow,
|
UUID: p.Users[i].Uuid,
|
||||||
UUID: p.Users[i].Uuid,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
err = in.(*vless.Inbound).AddUsers(us)
|
|
||||||
} else {
|
|
||||||
us := make([]option.VMessUser, len(p.Users))
|
|
||||||
for i := range p.Users {
|
|
||||||
us[i] = option.VMessUser{
|
|
||||||
Name: p.Users[i].Uuid,
|
|
||||||
UUID: p.Users[i].Uuid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = in.(*vmess.Inbound).AddUsers(us)
|
|
||||||
}
|
}
|
||||||
|
err = in.(*vless.Inbound).AddUsers(us)
|
||||||
|
case "vmess":
|
||||||
|
us := make([]option.VMessUser, len(p.Users))
|
||||||
|
for i := range p.Users {
|
||||||
|
us[i] = option.VMessUser{
|
||||||
|
Name: p.Users[i].Uuid,
|
||||||
|
UUID: p.Users[i].Uuid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = in.(*vmess.Inbound).AddUsers(us)
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
us := make([]option.ShadowsocksUser, len(p.Users))
|
us := make([]option.ShadowsocksUser, len(p.Users))
|
||||||
for i := range p.Users {
|
for i := range p.Users {
|
||||||
@@ -68,6 +73,18 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = in.(*trojan.Inbound).AddUsers(us)
|
err = in.(*trojan.Inbound).AddUsers(us)
|
||||||
|
case "tuic":
|
||||||
|
us := make([]option.TUICUser, len(p.Users))
|
||||||
|
id := make([]int, len(p.Users))
|
||||||
|
for i := range p.Users {
|
||||||
|
us[i] = option.TUICUser{
|
||||||
|
Name: p.Users[i].Uuid,
|
||||||
|
UUID: p.Users[i].Uuid,
|
||||||
|
Password: p.Users[i].Uuid,
|
||||||
|
}
|
||||||
|
id[i] = p.Users[i].Id
|
||||||
|
}
|
||||||
|
err = in.(*tuic.Inbound).AddUsers(us, id)
|
||||||
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 {
|
||||||
@@ -88,6 +105,15 @@ func (b *Sing) AddUsers(p *core.AddUsersParams) (added int, err error) {
|
|||||||
id[i] = p.Users[i].Id
|
id[i] = p.Users[i].Id
|
||||||
}
|
}
|
||||||
err = in.(*hysteria2.Inbound).AddUsers(us, id)
|
err = in.(*hysteria2.Inbound).AddUsers(us, id)
|
||||||
|
case "anytls":
|
||||||
|
us := make([]option.AnyTLSUser, len(p.Users))
|
||||||
|
for i := range p.Users {
|
||||||
|
us[i] = option.AnyTLSUser{
|
||||||
|
Name: p.Users[i].Uuid,
|
||||||
|
Password: p.Users[i].Uuid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = in.(*anytls.Inbound).AddUsers(us)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -108,14 +134,47 @@ func (b *Sing) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int6
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Sing) GetUserTrafficSlice(tag string, reset bool) ([]panel.UserTraffic, error) {
|
||||||
|
trafficSlice := make([]panel.UserTraffic, 0)
|
||||||
|
hook := b.hookServer
|
||||||
|
b.users.mapLock.RLock()
|
||||||
|
defer b.users.mapLock.RUnlock()
|
||||||
|
if v, ok := hook.counter.Load(tag); ok {
|
||||||
|
c := v.(*counter.TrafficCounter)
|
||||||
|
c.Counters.Range(func(key, value interface{}) bool {
|
||||||
|
uuid := key.(string)
|
||||||
|
traffic := value.(*counter.TrafficStorage)
|
||||||
|
up := traffic.UpCounter.Load()
|
||||||
|
down := traffic.DownCounter.Load()
|
||||||
|
if up+down > b.nodeReportMinTrafficBytes[tag] {
|
||||||
|
if reset {
|
||||||
|
traffic.UpCounter.Store(0)
|
||||||
|
traffic.DownCounter.Store(0)
|
||||||
|
}
|
||||||
|
trafficSlice = append(trafficSlice, panel.UserTraffic{
|
||||||
|
UID: b.users.uidMap[uuid],
|
||||||
|
Upload: up,
|
||||||
|
Download: down,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if len(trafficSlice) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return trafficSlice, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
type UserDeleter interface {
|
type UserDeleter interface {
|
||||||
DelUsers(uuid []string) error
|
DelUsers(uuid []string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Sing) DelUsers(users []panel.UserInfo, tag string) error {
|
func (b *Sing) DelUsers(users []panel.UserInfo, tag string, info *panel.NodeInfo) error {
|
||||||
var del UserDeleter
|
var del UserDeleter
|
||||||
if i, ok := b.inbounds[tag]; ok {
|
if i, found := b.box.Inbound().Get(tag); found {
|
||||||
switch i.Type() {
|
switch info.Type {
|
||||||
case "vmess":
|
case "vmess":
|
||||||
del = i.(*vmess.Inbound)
|
del = i.(*vmess.Inbound)
|
||||||
case "vless":
|
case "vless":
|
||||||
@@ -124,17 +183,23 @@ func (b *Sing) DelUsers(users []panel.UserInfo, tag string) error {
|
|||||||
del = i.(*shadowsocks.MultiInbound)
|
del = i.(*shadowsocks.MultiInbound)
|
||||||
case "trojan":
|
case "trojan":
|
||||||
del = i.(*trojan.Inbound)
|
del = i.(*trojan.Inbound)
|
||||||
|
case "tuic":
|
||||||
|
del = i.(*tuic.Inbound)
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
del = i.(*hysteria.Inbound)
|
del = i.(*hysteria.Inbound)
|
||||||
case "hysteria2":
|
case "hysteria2":
|
||||||
del = i.(*hysteria2.Inbound)
|
del = i.(*hysteria2.Inbound)
|
||||||
|
case "anytls":
|
||||||
|
del = i.(*anytls.Inbound)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("the inbound not found")
|
return errors.New("the inbound not found")
|
||||||
}
|
}
|
||||||
uuids := make([]string, len(users))
|
uuids := make([]string, len(users))
|
||||||
|
b.users.mapLock.Lock()
|
||||||
|
defer b.users.mapLock.Unlock()
|
||||||
for i := range users {
|
for i := range users {
|
||||||
b.hookServer.ClearConn(tag, users[i].Uuid)
|
delete(b.users.uidMap, users[i].Uuid)
|
||||||
uuids[i] = users[i].Uuid
|
uuids[i] = users[i].Uuid
|
||||||
}
|
}
|
||||||
err := del.DelUsers(uuids)
|
err := del.DelUsers(uuids)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
"github.com/InazumaV/V2bX/common/rate"
|
"github.com/InazumaV/V2bX/common/rate"
|
||||||
"github.com/InazumaV/V2bX/limiter"
|
"github.com/InazumaV/V2bX/limiter"
|
||||||
|
|
||||||
@@ -39,17 +40,21 @@ type cachedReader struct {
|
|||||||
cache buf.MultiBuffer
|
cache buf.MultiBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *cachedReader) Cache(b *buf.Buffer) {
|
func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error {
|
||||||
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
|
mb, err := r.reader.ReadMultiBufferTimeout(deadline)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
r.Lock()
|
r.Lock()
|
||||||
if !mb.IsEmpty() {
|
if !mb.IsEmpty() {
|
||||||
r.cache, _ = buf.MergeMulti(r.cache, mb)
|
r.cache, _ = buf.MergeMulti(r.cache, mb)
|
||||||
}
|
}
|
||||||
b.Clear()
|
b.Clear()
|
||||||
rawBytes := b.Extend(buf.Size)
|
rawBytes := b.Extend(min(r.cache.Len(), b.Cap()))
|
||||||
n := r.cache.Copy(rawBytes)
|
n := r.cache.Copy(rawBytes)
|
||||||
b.Resize(0, int32(n))
|
b.Resize(0, int32(n))
|
||||||
r.Unlock()
|
r.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *cachedReader) readInternal() buf.MultiBuffer {
|
func (r *cachedReader) readInternal() buf.MultiBuffer {
|
||||||
@@ -94,22 +99,23 @@ func (r *cachedReader) Interrupt() {
|
|||||||
|
|
||||||
// DefaultDispatcher is a default implementation of Dispatcher.
|
// DefaultDispatcher is a default implementation of Dispatcher.
|
||||||
type DefaultDispatcher struct {
|
type DefaultDispatcher struct {
|
||||||
ohm outbound.Manager
|
ohm outbound.Manager
|
||||||
router routing.Router
|
router routing.Router
|
||||||
policy policy.Manager
|
policy policy.Manager
|
||||||
stats stats.Manager
|
stats stats.Manager
|
||||||
dns dns.Client
|
fdns dns.FakeDNSEngine
|
||||||
fdns dns.FakeDNSEngine
|
Wm *WriterManager
|
||||||
|
Counter sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
d := new(DefaultDispatcher)
|
d := new(DefaultDispatcher)
|
||||||
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
|
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
|
||||||
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||||
d.fdns = fdns
|
d.fdns = fdns
|
||||||
})
|
})
|
||||||
return d.Init(config.(*Config), om, router, pm, sm, dc)
|
return d.Init(config.(*Config), om, router, pm, sm)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -118,12 +124,14 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes DefaultDispatcher.
|
// Init initializes DefaultDispatcher.
|
||||||
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error {
|
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error {
|
||||||
d.ohm = om
|
d.ohm = om
|
||||||
d.router = router
|
d.router = router
|
||||||
d.policy = pm
|
d.policy = pm
|
||||||
d.stats = sm
|
d.stats = sm
|
||||||
d.dns = dns
|
d.Wm = &WriterManager{
|
||||||
|
writers: make(map[string]map[*ManagedWriter]struct{}),
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,28 +195,33 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network) (*
|
|||||||
common.Interrupt(inboundLink.Reader)
|
common.Interrupt(inboundLink.Reader)
|
||||||
return nil, nil, nil, errors.New("Limited ", user.Email, " by conn or ip")
|
return nil, nil, nil, errors.New("Limited ", user.Email, " by conn or ip")
|
||||||
}
|
}
|
||||||
|
managedWriter := &ManagedWriter{
|
||||||
|
writer: uplinkWriter,
|
||||||
|
email: user.Email,
|
||||||
|
manager: d.Wm,
|
||||||
|
}
|
||||||
|
d.Wm.AddWriter(managedWriter)
|
||||||
|
inboundLink.Writer = managedWriter
|
||||||
if w != nil {
|
if w != nil {
|
||||||
inboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w)
|
inboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w)
|
||||||
outboundLink.Writer = rate.NewRateLimitWriter(outboundLink.Writer, w)
|
outboundLink.Writer = rate.NewRateLimitWriter(outboundLink.Writer, w)
|
||||||
}
|
}
|
||||||
p := d.policy.ForLevel(user.Level)
|
var t *counter.TrafficCounter
|
||||||
if p.Stats.UserUplink {
|
if c, ok := d.Counter.Load(sessionInbound.Tag); !ok {
|
||||||
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
t = counter.NewTrafficCounter()
|
||||||
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
|
d.Counter.Store(sessionInbound.Tag, t)
|
||||||
inboundLink.Writer = &SizeStatWriter{
|
} else {
|
||||||
Counter: c,
|
t = c.(*counter.TrafficCounter)
|
||||||
Writer: inboundLink.Writer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if p.Stats.UserDownlink {
|
|
||||||
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
|
inboundLink.Writer = &UploadTrafficWriter{
|
||||||
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
|
Counter: t.GetCounter(user.Email),
|
||||||
outboundLink.Writer = &SizeStatWriter{
|
Writer: inboundLink.Writer,
|
||||||
Counter: c,
|
}
|
||||||
Writer: outboundLink.Writer,
|
|
||||||
}
|
outboundLink.Writer = &DownloadTrafficWriter{
|
||||||
}
|
Counter: t.GetCounter(user.Email),
|
||||||
|
Writer: outboundLink.Writer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +371,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||||||
protocol = resComp.ProtocolForDomainResult()
|
protocol = resComp.ProtocolForDomainResult()
|
||||||
}
|
}
|
||||||
isFakeIP := false
|
isFakeIP := false
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && ob.Target.Address.Family().IsIP() && fkr0.IsIPInIPPool(ob.Target.Address) {
|
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(ob.Target.Address) {
|
||||||
isFakeIP = true
|
isFakeIP = true
|
||||||
}
|
}
|
||||||
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
|
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
|
||||||
@@ -366,7 +379,6 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||||||
} else {
|
} else {
|
||||||
ob.Target = destination
|
ob.Target = destination
|
||||||
}
|
}
|
||||||
destination.Address.Family()
|
|
||||||
}
|
}
|
||||||
d.routedDispatch(ctx, outbound, destination, nil, content.Protocol)
|
d.routedDispatch(ctx, outbound, destination, nil, content.Protocol)
|
||||||
}
|
}
|
||||||
@@ -375,7 +387,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
|
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
|
||||||
payload := buf.New()
|
payload := buf.NewWithSize(32767)
|
||||||
defer payload.Release()
|
defer payload.Release()
|
||||||
|
|
||||||
sniffer := NewSniffer(ctx)
|
sniffer := NewSniffer(ctx)
|
||||||
@@ -387,26 +399,36 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentResult, contentErr := func() (SniffResult, error) {
|
contentResult, contentErr := func() (SniffResult, error) {
|
||||||
|
cacheDeadline := 200 * time.Millisecond
|
||||||
totalAttempt := 0
|
totalAttempt := 0
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
default:
|
default:
|
||||||
totalAttempt++
|
cachingStartingTimeStamp := time.Now()
|
||||||
if totalAttempt > 2 {
|
err := cReader.Cache(payload, cacheDeadline)
|
||||||
return nil, errSniffingTimeout
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
cachingTimeElapsed := time.Since(cachingStartingTimeStamp)
|
||||||
|
cacheDeadline -= cachingTimeElapsed
|
||||||
|
|
||||||
cReader.Cache(payload)
|
|
||||||
if !payload.IsEmpty() {
|
if !payload.IsEmpty() {
|
||||||
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
|
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
|
||||||
if err != common.ErrNoClue {
|
switch err {
|
||||||
|
case common.ErrNoClue: // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not
|
||||||
|
totalAttempt++
|
||||||
|
case protocol.ErrProtoNeedMoreData: // Protocol Need More Data: protocol matches, but need more data to complete sniffing
|
||||||
|
// in this case, do not add totalAttempt(allow to read until timeout)
|
||||||
|
default:
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
totalAttempt++
|
||||||
}
|
}
|
||||||
if payload.IsFull() {
|
if totalAttempt >= 2 || cacheDeadline <= 0 {
|
||||||
return nil, errUnknownContent
|
return nil, errSniffingTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -423,29 +445,10 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
|||||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination, l *limiter.Limiter, protocol string) {
|
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination, l *limiter.Limiter, protocol string) {
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
ob := outbounds[len(outbounds)-1]
|
ob := outbounds[len(outbounds)-1]
|
||||||
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
|
||||||
proxied := hosts.LookupHosts(ob.Target.String())
|
|
||||||
if proxied != nil {
|
|
||||||
ro := ob.RouteTarget == destination
|
|
||||||
destination.Address = *proxied
|
|
||||||
if ro {
|
|
||||||
ob.RouteTarget = destination
|
|
||||||
} else {
|
|
||||||
ob.Target = destination
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionInbound := session.InboundFromContext(ctx)
|
sessionInbound := session.InboundFromContext(ctx)
|
||||||
if sessionInbound.User != nil {
|
if sessionInbound.User != nil {
|
||||||
if l != nil {
|
if l == nil {
|
||||||
// del connect count
|
|
||||||
if destination.Network == net.Network_TCP {
|
|
||||||
defer func() {
|
|
||||||
l.ConnLimiter.DelConnCount(sessionInbound.User.Email, sessionInbound.Source.Address.IP().String())
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var err error
|
var err error
|
||||||
l, err = limiter.GetLimiter(sessionInbound.Tag)
|
l, err = limiter.GetLimiter(sessionInbound.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -504,7 +507,11 @@ 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
|
||||||
errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
|
if route.GetRuleTag() == "" {
|
||||||
|
errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
|
||||||
|
} else {
|
||||||
|
errors.LogInfo(ctx, "Hit route rule: [", route.GetRuleTag(), "] so taking detour [", outTag, "] for [", destination, "]")
|
||||||
|
}
|
||||||
handler = h
|
handler = h
|
||||||
} else {
|
} else {
|
||||||
errors.LogWarning(ctx, "non existing outTag: ", outTag)
|
errors.LogWarning(ctx, "non existing outTag: ", outTag)
|
||||||
@@ -514,10 +521,6 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if handler == nil {
|
|
||||||
handler = d.ohm.GetHandler(inTag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
handler = d.ohm.GetDefaultHandler()
|
handler = d.ohm.GetDefaultHandler()
|
||||||
}
|
}
|
||||||
@@ -529,6 +532,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ob.Tag = handler.Tag()
|
||||||
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
||||||
if tag := handler.Tag(); tag != "" {
|
if tag := handler.Tag(); tag != "" {
|
||||||
if inTag == "" {
|
if inTag == "" {
|
||||||
|
|||||||
61
core/xray/app/dispatcher/linkmanager.go
Normal file
61
core/xray/app/dispatcher/linkmanager.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
sync "sync"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WriterManager struct {
|
||||||
|
writers map[string]map[*ManagedWriter]struct{}
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WriterManager) AddWriter(writer *ManagedWriter) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
if _, exists := m.writers[writer.email]; !exists {
|
||||||
|
m.writers[writer.email] = make(map[*ManagedWriter]struct{})
|
||||||
|
}
|
||||||
|
m.writers[writer.email][writer] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WriterManager) RemoveWriter(writer *ManagedWriter) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
if _, exists := m.writers[writer.email]; !exists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(m.writers[writer.email], writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WriterManager) RemoveWritersForUser(email string) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
if _, exists := m.writers[email]; !exists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for writer := range m.writers[email] {
|
||||||
|
delete(m.writers[email], writer)
|
||||||
|
common.Close(writer.writer)
|
||||||
|
}
|
||||||
|
delete(m.writers, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ManagedWriter struct {
|
||||||
|
writer buf.Writer
|
||||||
|
email string
|
||||||
|
manager *WriterManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ManagedWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
return w.writer.WriteMultiBuffer(mb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ManagedWriter) Close() error {
|
||||||
|
w.manager.RemoveWriter(w)
|
||||||
|
return common.Close(w.writer)
|
||||||
|
}
|
||||||
@@ -1,25 +1,43 @@
|
|||||||
package dispatcher
|
package dispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
"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/features/stats"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SizeStatWriter struct {
|
type UploadTrafficWriter struct {
|
||||||
Counter stats.Counter
|
Counter *counter.TrafficStorage
|
||||||
Writer buf.Writer
|
Writer buf.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *SizeStatWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
type DownloadTrafficWriter struct {
|
||||||
w.Counter.Add(int64(mb.Len()))
|
Counter *counter.TrafficStorage
|
||||||
|
Writer buf.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *UploadTrafficWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
w.Counter.UpCounter.Add(int64(mb.Len()))
|
||||||
return w.Writer.WriteMultiBuffer(mb)
|
return w.Writer.WriteMultiBuffer(mb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *SizeStatWriter) Close() error {
|
func (w *UploadTrafficWriter) Close() error {
|
||||||
return common.Close(w.Writer)
|
return common.Close(w.Writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *SizeStatWriter) Interrupt() {
|
func (w *UploadTrafficWriter) Interrupt() {
|
||||||
|
common.Interrupt(w.Writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *DownloadTrafficWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
w.Counter.DownCounter.Add(int64(mb.Len()))
|
||||||
|
return w.Writer.WriteMultiBuffer(mb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *DownloadTrafficWriter) Close() error {
|
||||||
|
return common.Close(w.Writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *DownloadTrafficWriter) Interrupt() {
|
||||||
common.Interrupt(w.Writer)
|
common.Interrupt(w.Writer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
"github.com/goccy/go-json"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
coreConf "github.com/xtls/xray-core/infra/conf"
|
coreConf "github.com/xtls/xray-core/infra/conf"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"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/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
coreConf "github.com/xtls/xray-core/infra/conf"
|
coreConf "github.com/xtls/xray-core/infra/conf"
|
||||||
@@ -133,12 +134,14 @@ func buildInbound(option *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*
|
|||||||
in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{
|
in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{
|
||||||
Dest: d,
|
Dest: d,
|
||||||
Xver: xver,
|
Xver: xver,
|
||||||
|
Show: false,
|
||||||
ServerNames: []string{v.TlsSettings.ServerName},
|
ServerNames: []string{v.TlsSettings.ServerName},
|
||||||
PrivateKey: v.TlsSettings.PrivateKey,
|
PrivateKey: v.TlsSettings.PrivateKey,
|
||||||
MinClientVer: v.RealityConfig.MinClientVer,
|
MinClientVer: v.RealityConfig.MinClientVer,
|
||||||
MaxClientVer: v.RealityConfig.MaxClientVer,
|
MaxClientVer: v.RealityConfig.MaxClientVer,
|
||||||
MaxTimeDiff: uint64(mtd.Microseconds()),
|
MaxTimeDiff: uint64(mtd.Microseconds()),
|
||||||
ShortIds: []string{v.TlsSettings.ShortId},
|
ShortIds: []string{v.TlsSettings.ShortId},
|
||||||
|
Mldsa65Seed: v.TlsSettings.Mldsa65Seed,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type DNSConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Xray) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error {
|
func (c *Xray) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error {
|
||||||
|
c.nodeReportMinTrafficBytes[tag] = config.ReportMinTraffic * 1024
|
||||||
err := updateDNSConfig(info)
|
err := updateDNSConfig(info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("build dns error: %s", err)
|
return fmt.Errorf("build dns error: %s", err)
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package xray
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
conf2 "github.com/InazumaV/V2bX/conf"
|
conf2 "github.com/InazumaV/V2bX/conf"
|
||||||
"github.com/goccy/go-json"
|
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/infra/conf"
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/api/panel"
|
"github.com/InazumaV/V2bX/api/panel"
|
||||||
|
"github.com/InazumaV/V2bX/common/counter"
|
||||||
"github.com/InazumaV/V2bX/common/format"
|
"github.com/InazumaV/V2bX/common/format"
|
||||||
vCore "github.com/InazumaV/V2bX/core"
|
vCore "github.com/InazumaV/V2bX/core"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
@@ -27,51 +28,66 @@ func (c *Xray) GetUserManager(tag string) (proxy.UserManager, error) {
|
|||||||
return userManager, nil
|
return userManager, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Xray) DelUsers(users []panel.UserInfo, tag string) error {
|
func (c *Xray) DelUsers(users []panel.UserInfo, tag string, _ *panel.NodeInfo) error {
|
||||||
userManager, err := c.GetUserManager(tag)
|
userManager, err := c.GetUserManager(tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get user manager error: %s", err)
|
return fmt.Errorf("get user manager error: %s", err)
|
||||||
}
|
}
|
||||||
var up, down, user string
|
var user string
|
||||||
|
c.users.mapLock.Lock()
|
||||||
|
defer c.users.mapLock.Unlock()
|
||||||
for i := range users {
|
for i := range users {
|
||||||
user = format.UserTag(tag, users[i].Uuid)
|
user = format.UserTag(tag, users[i].Uuid)
|
||||||
err = userManager.RemoveUser(context.Background(), user)
|
err = userManager.RemoveUser(context.Background(), user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
up = "user>>>" + user + ">>>traffic>>>uplink"
|
delete(c.users.uidMap, user)
|
||||||
down = "user>>>" + user + ">>>traffic>>>downlink"
|
c.dispatcher.Counter.Delete(user)
|
||||||
c.shm.UnregisterCounter(up)
|
c.dispatcher.Wm.RemoveWritersForUser(user)
|
||||||
c.shm.UnregisterCounter(down)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Xray) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
|
func (x *Xray) GetUserTrafficSlice(tag string, reset bool) ([]panel.UserTraffic, error) {
|
||||||
upName := "user>>>" + format.UserTag(tag, uuid) + ">>>traffic>>>uplink"
|
trafficSlice := make([]panel.UserTraffic, 0)
|
||||||
downName := "user>>>" + format.UserTag(tag, uuid) + ">>>traffic>>>downlink"
|
x.users.mapLock.RLock()
|
||||||
upCounter := c.shm.GetCounter(upName)
|
defer x.users.mapLock.RUnlock()
|
||||||
downCounter := c.shm.GetCounter(downName)
|
if v, ok := x.dispatcher.Counter.Load(tag); ok {
|
||||||
if reset {
|
c := v.(*counter.TrafficCounter)
|
||||||
if upCounter != nil {
|
c.Counters.Range(func(key, value interface{}) bool {
|
||||||
up = upCounter.Set(0)
|
email := key.(string)
|
||||||
}
|
traffic := value.(*counter.TrafficStorage)
|
||||||
if downCounter != nil {
|
up := traffic.UpCounter.Load()
|
||||||
down = downCounter.Set(0)
|
down := traffic.DownCounter.Load()
|
||||||
}
|
if up+down > x.nodeReportMinTrafficBytes[tag] {
|
||||||
} else {
|
if reset {
|
||||||
if upCounter != nil {
|
traffic.UpCounter.Store(0)
|
||||||
up = upCounter.Value()
|
traffic.DownCounter.Store(0)
|
||||||
}
|
}
|
||||||
if downCounter != nil {
|
trafficSlice = append(trafficSlice, panel.UserTraffic{
|
||||||
down = downCounter.Value()
|
UID: x.users.uidMap[email],
|
||||||
|
Upload: up,
|
||||||
|
Download: down,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if len(trafficSlice) == 0 {
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
return trafficSlice, nil
|
||||||
}
|
}
|
||||||
return up, down
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Xray) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
|
func (c *Xray) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
|
||||||
users := make([]*protocol.User, 0, len(p.Users))
|
c.users.mapLock.Lock()
|
||||||
|
defer c.users.mapLock.Unlock()
|
||||||
|
for i := range p.Users {
|
||||||
|
c.users.uidMap[format.UserTag(p.Tag, p.Users[i].Uuid)] = p.Users[i].Id
|
||||||
|
}
|
||||||
|
var users []*protocol.User
|
||||||
switch p.NodeInfo.Type {
|
switch p.NodeInfo.Type {
|
||||||
case "vmess":
|
case "vmess":
|
||||||
users = buildVmessUsers(p.Tag, p.Users)
|
users = buildVmessUsers(p.Tag, p.Users)
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"encoding/json/v2"
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
vCore "github.com/InazumaV/V2bX/core"
|
vCore "github.com/InazumaV/V2bX/core"
|
||||||
"github.com/InazumaV/V2bX/core/xray/app/dispatcher"
|
"github.com/InazumaV/V2bX/core/xray/app/dispatcher"
|
||||||
_ "github.com/InazumaV/V2bX/core/xray/distro/all"
|
_ "github.com/InazumaV/V2bX/core/xray/distro/all"
|
||||||
"github.com/goccy/go-json"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/xtls/xray-core/app/proxyman"
|
"github.com/xtls/xray-core/app/proxyman"
|
||||||
"github.com/xtls/xray-core/app/stats"
|
"github.com/xtls/xray-core/app/stats"
|
||||||
@@ -30,16 +31,29 @@ func init() {
|
|||||||
|
|
||||||
// Xray Structure
|
// Xray Structure
|
||||||
type Xray struct {
|
type Xray struct {
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
Server *core.Instance
|
Server *core.Instance
|
||||||
ihm inbound.Manager
|
ihm inbound.Manager
|
||||||
ohm outbound.Manager
|
ohm outbound.Manager
|
||||||
shm statsFeature.Manager
|
shm statsFeature.Manager
|
||||||
dispatcher *dispatcher.DefaultDispatcher
|
dispatcher *dispatcher.DefaultDispatcher
|
||||||
|
users *UserMap
|
||||||
|
nodeReportMinTrafficBytes map[string]int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserMap struct {
|
||||||
|
uidMap map[string]int
|
||||||
|
mapLock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(c *conf.CoreConfig) (vCore.Core, error) {
|
func New(c *conf.CoreConfig) (vCore.Core, error) {
|
||||||
return &Xray{Server: getCore(c.XrayConfig)}, nil
|
return &Xray{
|
||||||
|
Server: getCore(c.XrayConfig),
|
||||||
|
users: &UserMap{
|
||||||
|
uidMap: make(map[string]int),
|
||||||
|
},
|
||||||
|
nodeReportMinTrafficBytes: make(map[string]int64),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseConnectionConfig(c *conf.XrayConnectionConfig) (policy *coreConf.Policy) {
|
func parseConnectionConfig(c *conf.XrayConnectionConfig) (policy *coreConf.Policy) {
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
{
|
{
|
||||||
"Type": "sing",
|
"Type": "sing",
|
||||||
"Log": {
|
"Log": {
|
||||||
"Level": "error",
|
"Level": "info",
|
||||||
"Timestamp": true
|
"Timestamp": true
|
||||||
},
|
},
|
||||||
"NTP": {
|
"NTP": {
|
||||||
"Enable": true,
|
"Enable": false,
|
||||||
"Server": "time.apple.com",
|
"Server": "time.apple.com",
|
||||||
"ServerPort": 0
|
"ServerPort": 0
|
||||||
}
|
},
|
||||||
|
"OriginalPath": "/etc/V2bX/sing_origin.json"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Nodes": [
|
"Nodes": [
|
||||||
@@ -27,20 +28,19 @@
|
|||||||
"Timeout": 30,
|
"Timeout": 30,
|
||||||
"ListenIP": "0.0.0.0",
|
"ListenIP": "0.0.0.0",
|
||||||
"SendIP": "0.0.0.0",
|
"SendIP": "0.0.0.0",
|
||||||
"EnableProxyProtocol": false,
|
"DeviceOnlineMinTraffic": 200,
|
||||||
"EnableDNS": true,
|
"MinReportTraffic": 0,
|
||||||
"DomainStrategy": "ipv4_only",
|
"TCPFastOpen": false,
|
||||||
"LimitConfig": {
|
"SniffEnabled": true,
|
||||||
"EnableRealtime": false,
|
"CertConfig": {
|
||||||
"SpeedLimit": 0,
|
"CertMode": "self",
|
||||||
"IPLimit": 0,
|
"RejectUnknownSni": false,
|
||||||
"ConnLimit": 0,
|
"CertDomain": "example.com",
|
||||||
"EnableDynamicSpeedLimit": false,
|
"CertFile": "/etc/V2bX/fullchain.cer",
|
||||||
"DynamicSpeedLimitConfig": {
|
"KeyFile": "/etc/V2bX/cert.key",
|
||||||
"Periodic": 60,
|
"Provider": "cloudflare",
|
||||||
"Traffic": 1000,
|
"DNSEnv": {
|
||||||
"SpeedLimit": 100,
|
"EnvName": "env1"
|
||||||
"ExpireTime": 60
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,261 +0,0 @@
|
|||||||
{
|
|
||||||
"Log": {
|
|
||||||
// V2bX 的日志配置,独立于各 Core 的 log 配置
|
|
||||||
|
|
||||||
// 日志等级,info, warn, error, none
|
|
||||||
"Level": "error",
|
|
||||||
// 日志输出路径,默认输出到标准输出
|
|
||||||
"Output": ""
|
|
||||||
},
|
|
||||||
"Cores": [
|
|
||||||
{
|
|
||||||
// Core类型
|
|
||||||
"Type": "sing",
|
|
||||||
// Core标识名,可选,如果需要启动多个同类型内核则必填
|
|
||||||
"Name": "sing1",
|
|
||||||
"Log": {
|
|
||||||
// 同 SingBox log 部分配置
|
|
||||||
|
|
||||||
"Level": "error",
|
|
||||||
"Timestamp": true
|
|
||||||
},
|
|
||||||
"NTP": {
|
|
||||||
// 同 SingBox ntp 部分配置
|
|
||||||
// VMess VLESS 建议开启
|
|
||||||
"Enable": true,
|
|
||||||
"Server": "time.apple.com",
|
|
||||||
"ServerPort": 0
|
|
||||||
},
|
|
||||||
"DnsConfigPath": "/etc/V2bX/dns.json",
|
|
||||||
// SingBox源配置文件目录,用于引用标准SingBox配置文件
|
|
||||||
"OriginalPath": "/etc/V2bX/sing_origin.json",
|
|
||||||
// 在删除用户时清理已建立的连接
|
|
||||||
"EnableConnClear": false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Type": "sing",
|
|
||||||
"Name": "sing2",
|
|
||||||
"Log": {
|
|
||||||
"Level": "info",
|
|
||||||
"Timestamp": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Type": "xray",
|
|
||||||
"Log": {
|
|
||||||
// 同 Xray-core log 部分配置
|
|
||||||
|
|
||||||
"Level": "error"
|
|
||||||
},
|
|
||||||
// 静态资源文件目录
|
|
||||||
"AssetPath": "",
|
|
||||||
// DNS配置文件目录
|
|
||||||
"DnsConfigPath": "",
|
|
||||||
// 路由配置文件目录
|
|
||||||
"RouteConfigPath": "",
|
|
||||||
// 本地策略相关配置
|
|
||||||
"ConnectionConfig": {
|
|
||||||
// 详见 https://xtls.github.io/config/policy.html#levelpolicyobject
|
|
||||||
|
|
||||||
"handshake": 4,
|
|
||||||
"connIdle": 300,
|
|
||||||
"uplinkOnly": 2,
|
|
||||||
"downlinkOnly": 5,
|
|
||||||
"statsUserUplink": false,
|
|
||||||
"statsUserDownlink": false,
|
|
||||||
"bufferSize": 4
|
|
||||||
},
|
|
||||||
// Inbound配置文件目录
|
|
||||||
"InboundConfigPath": "",
|
|
||||||
// Outbound配置文件目录
|
|
||||||
"OutboundConfigPath": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Nodes": [
|
|
||||||
// Node配置有两种写法
|
|
||||||
{
|
|
||||||
// 写法1
|
|
||||||
// sing内核
|
|
||||||
|
|
||||||
// Node标识名,便于查看日志,不填将通过下发的节点配置自动生成
|
|
||||||
// 务必注意不要重复,否则会出现问题
|
|
||||||
"Name": "sing_node1",
|
|
||||||
|
|
||||||
// 要使用的Core的类型
|
|
||||||
// 如果填写了CoreName可不填,但单内核务必填写
|
|
||||||
// 建议视情况填写Core和CoreName其中一个,如果均没有填写将随机选择支持的内核
|
|
||||||
"Core": "sing",
|
|
||||||
|
|
||||||
// 要使用的Core的标识名,如果没有定义多个同类型内核可不填
|
|
||||||
"CoreName": "sing1",
|
|
||||||
|
|
||||||
// API接口地址
|
|
||||||
"ApiHost": "http://127.0.0.1",
|
|
||||||
|
|
||||||
// API密钥,即Token
|
|
||||||
"ApiKey": "test",
|
|
||||||
|
|
||||||
// 节点ID
|
|
||||||
"NodeID": 33,
|
|
||||||
|
|
||||||
// 节点类型
|
|
||||||
"NodeType": "shadowsocks",
|
|
||||||
|
|
||||||
// 请求超时时间
|
|
||||||
"Timeout": 30,
|
|
||||||
|
|
||||||
// 监听IP
|
|
||||||
"ListenIP": "0.0.0.0",
|
|
||||||
|
|
||||||
// 发送IP
|
|
||||||
"SendIP": "0.0.0.0",
|
|
||||||
|
|
||||||
// 开启 Proxy Protocol,参见 https://github.com/haproxy/haproxy/blob/master/doc/proxy-protocol.txt
|
|
||||||
"EnableProxyProtocol": false,
|
|
||||||
|
|
||||||
// 开启 TCP Fast Open
|
|
||||||
"EnableTFO": true,
|
|
||||||
|
|
||||||
// 开启 DNS
|
|
||||||
"EnableDNS" : true,
|
|
||||||
// 设置 Domain Strategy 需要开启 DNS ,默认 AsIS
|
|
||||||
// 可选 prefer_ipv4 / prefer_ipv6 / ipv4_only / ipv6_only
|
|
||||||
"DomainStrategy": "ipv4_only",
|
|
||||||
|
|
||||||
// 限制器相关配置
|
|
||||||
"LimitConfig": {
|
|
||||||
// 开启实时连接数及IP数限制
|
|
||||||
"EnableRealtime": false,
|
|
||||||
|
|
||||||
// 用户速度限制
|
|
||||||
"SpeedLimit": 0,
|
|
||||||
|
|
||||||
// 用户IP限制
|
|
||||||
"IPLimit": 0,
|
|
||||||
|
|
||||||
// 用户连接数限制
|
|
||||||
"ConnLimit": 0,
|
|
||||||
|
|
||||||
// 开启动态限速
|
|
||||||
"EnableDynamicSpeedLimit": false,
|
|
||||||
|
|
||||||
// 动态限速相关配置
|
|
||||||
"DynamicSpeedLimitConfig": {
|
|
||||||
// 检查周期
|
|
||||||
"Periodic": 60,
|
|
||||||
|
|
||||||
// 检查周期内触发限制的流量数
|
|
||||||
"Traffic": 1000,
|
|
||||||
|
|
||||||
// 触发限制后的速度限制
|
|
||||||
"SpeedLimit": 100,
|
|
||||||
|
|
||||||
// 速度限制过期时间
|
|
||||||
"ExpireTime": 60
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 证书相关配置
|
|
||||||
"CertConfig": {
|
|
||||||
// 证书申请模式,none、http、dns、self
|
|
||||||
"CertMode": "none",
|
|
||||||
|
|
||||||
"RejectUnknownSni": false,
|
|
||||||
|
|
||||||
// 证书域名
|
|
||||||
"CertDomain": "test.com",
|
|
||||||
|
|
||||||
// 证书文件目录
|
|
||||||
"CertFile": "/etc/V2bX/cert/1.pem",
|
|
||||||
|
|
||||||
// 密钥文件目录
|
|
||||||
"KeyFile": "/etc/V2bX/cert/1.key",
|
|
||||||
|
|
||||||
// 申请证书时使用的用户邮箱
|
|
||||||
"Email": "1@test.com",
|
|
||||||
|
|
||||||
// DNS解析提供者
|
|
||||||
"Provider": "cloudflare",
|
|
||||||
|
|
||||||
// DNS解析提供者的环境变量,详见 https://go-acme.github.io/lego/dns/
|
|
||||||
"DNSEnv": {
|
|
||||||
"EnvName": "env1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// xray内核
|
|
||||||
|
|
||||||
"Name": "xray_node1",
|
|
||||||
"Core": "xray",
|
|
||||||
"CoreName": "",
|
|
||||||
"ApiHost": "http://127.0.0.1",
|
|
||||||
"ApiKey": "test",
|
|
||||||
"NodeID": 33,
|
|
||||||
"NodeType": "shadowsocks",
|
|
||||||
"Timeout": 30,
|
|
||||||
"ListenIP": "0.0.0.0",
|
|
||||||
"SendIP": "0.0.0.0",
|
|
||||||
"EnableProxyProtocol": true,
|
|
||||||
"EnableTFO": true,
|
|
||||||
// 以上同 sing
|
|
||||||
|
|
||||||
// 开启自定义DNS
|
|
||||||
"EnableDNS": false,
|
|
||||||
|
|
||||||
// DNS解析类型,AsIs、UseIP、UseIPv4、UseIPv6
|
|
||||||
"DNSType": "AsIs",
|
|
||||||
|
|
||||||
// 开启udp over tcp
|
|
||||||
"EnableUot": false,
|
|
||||||
|
|
||||||
// 禁用IVCheck
|
|
||||||
"DisableIVCheck": false,
|
|
||||||
|
|
||||||
// 禁用嗅探
|
|
||||||
"DisableSniffing": false,
|
|
||||||
|
|
||||||
// 开启回落
|
|
||||||
"EnableFallback": false,
|
|
||||||
|
|
||||||
// 回落相关配置
|
|
||||||
"FallBackConfigs":{
|
|
||||||
// 详见 https://xtls.github.io/config/features/fallback.html#fallbackobject
|
|
||||||
|
|
||||||
"SNI": "",
|
|
||||||
"Alpn": "",
|
|
||||||
"Path": "",
|
|
||||||
"Dest": "",
|
|
||||||
"ProxyProtocolVer": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// 写法2
|
|
||||||
|
|
||||||
// 类似旧配置文件 ApiConfig 部分
|
|
||||||
"ApiConfig": {
|
|
||||||
"ApiHost": "http://127.0.0.1",
|
|
||||||
"ApiKey": "test",
|
|
||||||
"NodeID": 33,
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
// 类似旧配置文件 ControllerConfig 部分
|
|
||||||
"Options": {
|
|
||||||
"Core": "sing",
|
|
||||||
"EnableProxyProtocol": true,
|
|
||||||
"EnableTFO": true,
|
|
||||||
"DomainStrategy": "ipv4_only"
|
|
||||||
// More
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// 引用本地其他配置文件
|
|
||||||
"Include": "../example/config_full_node1.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// 通过Http引用远端配置文件
|
|
||||||
"Include": "http://127.0.0.1:11451/config_full_node1.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"Core": "xray",
|
|
||||||
"ApiHost": "https://127.0.0.1",
|
|
||||||
"ApiKey": "key",
|
|
||||||
"NodeID": 1,
|
|
||||||
"NodeType": "vmess",
|
|
||||||
"Timeout": 30,
|
|
||||||
"ListenIP": "0.0.0.0",
|
|
||||||
"SendIP": "0.0.0.0",
|
|
||||||
"EnableProxyProtocol": false,
|
|
||||||
"EnableTFO": true,
|
|
||||||
"DNSType": "ipv4_only"
|
|
||||||
}
|
|
||||||
302
go.mod
302
go.mod
@@ -1,44 +1,45 @@
|
|||||||
module github.com/InazumaV/V2bX
|
module github.com/InazumaV/V2bX
|
||||||
|
|
||||||
go 1.23
|
go 1.25
|
||||||
|
|
||||||
toolchain go1.23.2
|
toolchain go1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/apernet/hysteria/core/v2 v2.6.0
|
github.com/apernet/hysteria/core/v2 v2.6.2
|
||||||
github.com/apernet/hysteria/extras/v2 v2.6.0
|
github.com/apernet/hysteria/extras/v2 v2.6.2
|
||||||
github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7
|
github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431
|
||||||
github.com/beevik/ntp v1.2.0
|
github.com/beevik/ntp v1.4.4-0.20240716062501-06ef196b89ec
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.8.0
|
||||||
github.com/go-acme/lego/v4 v4.19.3-0.20241028134924-480950181787
|
github.com/go-acme/lego/v4 v4.21.1-0.20241220151055-ee7a9e4fa04f
|
||||||
github.com/go-resty/resty/v2 v2.15.3
|
github.com/go-resty/resty/v2 v2.16.2
|
||||||
github.com/goccy/go-json v0.10.3
|
github.com/hashicorp/go-multierror v1.1.2-0.20241119060415-613124da9385
|
||||||
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.6.0-beta.6
|
github.com/sagernet/sing v0.7.0-beta.2
|
||||||
github.com/sagernet/sing-box v1.10.4
|
github.com/sagernet/sing-box v1.12.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.9.1
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.19.0
|
||||||
github.com/xtls/xray-core v1.8.25-0.20241215123619-7d0a80b501d4
|
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||||
|
github.com/xtls/xray-core v1.250803.0
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/crypto v0.30.0
|
golang.org/x/crypto v0.40.0
|
||||||
golang.org/x/sys v0.28.0
|
golang.org/x/sys v0.34.0
|
||||||
google.golang.org/protobuf v1.35.2
|
google.golang.org/protobuf v1.36.6
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/auth v0.9.3 // indirect
|
cloud.google.com/go/auth v0.13.0 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.5.1 // indirect
|
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||||
|
filippo.io/edwards25519 v1.1.0 // 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.14.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.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.2.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.2.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.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.29 // indirect
|
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||||
@@ -53,71 +54,90 @@ require (
|
|||||||
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/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.63.15 // indirect
|
github.com/akutz/memconn v0.1.0 // indirect
|
||||||
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
|
||||||
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.72 // indirect
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect
|
github.com/anytls/sing-anytls v0.0.9-0.20250508103614-8bc6dd599731 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.33 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.32.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 // indirect
|
github.com/aws/aws-sdk-go-v2/config v1.28.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // 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/accept-encoding v1.12.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.40.6 // indirect
|
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.43.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect
|
||||||
github.com/aws/smithy-go v1.20.4 // indirect
|
github.com/aws/smithy-go v1.22.1 // 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/benbjohnson/clock v1.3.0 // indirect
|
||||||
|
github.com/bits-and-blooms/bitset v1.13.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.23.0 // indirect
|
||||||
|
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // 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.4.0 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.104.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.112.0 // indirect
|
||||||
|
github.com/coder/websocket v1.8.13 // indirect
|
||||||
|
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // 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/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect
|
||||||
|
github.com/database64128/tfo-go/v2 v2.2.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // 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/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
|
||||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||||
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
||||||
github.com/exoscale/egoscale/v3 v3.1.5 // indirect
|
github.com/exoscale/egoscale/v3 v3.1.7 // 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/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/gaissmai/bart v0.11.1 // 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.1.0 // indirect
|
github.com/go-chi/chi/v5 v5.2.2 // indirect
|
||||||
github.com/go-chi/render v1.0.3 // 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/v4 v4.0.4 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.3 // 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/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.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-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/go-viper/mapstructure/v2 v2.2.1 // 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/goccy/go-json v0.10.4 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
|
||||||
github.com/gofrs/flock v0.12.1 // indirect
|
github.com/gofrs/flock v0.12.1 // indirect
|
||||||
github.com/gofrs/uuid/v5 v5.3.0 // indirect
|
github.com/gofrs/uuid/v5 v5.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 // 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/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // 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.8 // 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.4 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
|
||||||
github.com/gophercloud/gophercloud v1.14.0 // indirect
|
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
||||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||||
|
github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 // indirect
|
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.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
|
||||||
@@ -126,47 +146,54 @@ require (
|
|||||||
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.2 // indirect
|
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 // indirect
|
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||||
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 // indirect
|
||||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||||
|
github.com/illarion/gonotify/v2 v2.0.3 // 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/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f // 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/jsimonetti/rtnetlink v1.4.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.9 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||||
|
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // 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/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/libdns/alidns v1.0.3 // indirect
|
github.com/libdns/alidns v1.0.5-libdns.v1.beta1 // indirect
|
||||||
github.com/libdns/cloudflare v0.1.1 // indirect
|
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 // indirect
|
||||||
github.com/libdns/libdns v0.2.2 // indirect
|
github.com/libdns/libdns v1.1.0 // indirect
|
||||||
github.com/linode/linodego v1.40.0 // indirect
|
github.com/linode/linodego v1.44.0 // 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.4 // 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-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||||
github.com/mdlayher/socket v0.4.1 // indirect
|
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa // indirect
|
github.com/mdlayher/sdnotify v1.0.0 // indirect
|
||||||
github.com/mholt/acmez v1.2.0 // indirect
|
github.com/mdlayher/socket v0.5.1 // indirect
|
||||||
github.com/miekg/dns v1.1.62 // indirect
|
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
|
||||||
|
github.com/metacubex/utls v1.8.0 // indirect
|
||||||
|
github.com/mholt/acmez/v3 v3.1.2 // indirect
|
||||||
|
github.com/miekg/dns v1.1.67 // 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/go-ps v1.0.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
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/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
||||||
github.com/nrdcg/desec v0.8.0 // indirect
|
github.com/nrdcg/desec v0.10.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.3.0 // indirect
|
||||||
github.com/nrdcg/goinwx v0.10.0 // indirect
|
github.com/nrdcg/goinwx v0.10.0 // indirect
|
||||||
github.com/nrdcg/mailinabox v0.2.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
|
||||||
@@ -175,116 +202,131 @@ require (
|
|||||||
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/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.73.0 // indirect
|
github.com/oracle/oci-go-sdk/v65 v65.81.1 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
|
||||||
github.com/ovh/go-ovh v1.6.0 // 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.1.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
github.com/peterhellberg/link v1.2.0 // indirect
|
||||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
|
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // 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.1-0.20181226105442-5d4384ee4fb2 // 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/prometheus-community/pro-bing v0.4.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // 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/quic-go v0.54.0 // indirect
|
||||||
github.com/quic-go/quic-go v0.46.0 // indirect
|
github.com/refraction-networking/utls v1.8.0 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
github.com/regfish/regfish-dnsapi-go v0.1.1 // 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.10 // indirect
|
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||||
github.com/sacloud/go-http v0.1.8 // indirect
|
github.com/sacloud/go-http v0.1.8 // indirect
|
||||||
github.com/sacloud/iaas-api-go v1.12.0 // indirect
|
github.com/sacloud/iaas-api-go v1.14.0 // indirect
|
||||||
github.com/sacloud/packages-go v0.0.10 // indirect
|
github.com/sacloud/packages-go v0.0.10 // indirect
|
||||||
|
github.com/safchain/ethtool v0.3.0 // 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/cors v1.2.1 // indirect
|
github.com/sagernet/cors v1.2.1 // indirect
|
||||||
github.com/sagernet/fswatch v0.1.1 // indirect
|
github.com/sagernet/fswatch v0.1.1 // indirect
|
||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff // indirect
|
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb // 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.4 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/sagernet/quic-go v0.48.2-beta.1 // indirect
|
github.com/sagernet/quic-go v0.52.0-beta.1 // indirect
|
||||||
github.com/sagernet/sing-dns v0.4.0-beta.1 // indirect
|
github.com/sagernet/sing-mux v0.3.2 // indirect
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 // indirect
|
github.com/sagernet/sing-quic v0.5.0-beta.3 // indirect
|
||||||
github.com/sagernet/sing-quic v0.4.0-alpha.4 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.8 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
github.com/sagernet/sing-shadowsocks2 v0.2.1 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 // indirect
|
github.com/sagernet/sing-tun v0.7.0-beta.1 // indirect
|
||||||
github.com/sagernet/sing-tun v0.6.0-beta.2 // indirect
|
github.com/sagernet/sing-vmess v0.2.6 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.2.0-beta.1 // indirect
|
github.com/sagernet/smux v1.5.34-mod.2 // indirect
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
github.com/sagernet/tailscale v1.80.3-mod.5 // indirect
|
||||||
github.com/sagernet/utls v1.6.7 // indirect
|
github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.5 // indirect
|
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 // 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/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
||||||
github.com/selectel/domains-go v1.1.0 // indirect
|
github.com/selectel/domains-go v1.1.0 // indirect
|
||||||
github.com/selectel/go-selvpcclient/v3 v3.1.1 // indirect
|
github.com/selectel/go-selvpcclient/v3 v3.2.1 // indirect
|
||||||
github.com/shopspring/decimal v1.3.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.5 // indirect
|
github.com/softlayer/softlayer-go v1.1.7 // indirect
|
||||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||||
github.com/sony/gobreaker v0.5.0 // indirect
|
github.com/sony/gobreaker v0.5.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1002 // indirect
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 // indirect
|
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
||||||
|
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 // indirect
|
||||||
|
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
||||||
|
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
||||||
|
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
|
||||||
|
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
|
||||||
|
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065 // indirect
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/transip/gotransip/v6 v6.26.0 // 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/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||||
github.com/ultradns/ultradns-go-sdk v1.7.0-20240913052650-970ca9a // indirect
|
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // 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.3.0 // indirect
|
github.com/vishvananda/netlink v1.3.1 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.177 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
github.com/volcengine/volc-sdk-golang v1.0.189 // indirect
|
||||||
github.com/vultr/govultr/v3 v3.9.1 // indirect
|
github.com/vultr/govultr/v3 v3.9.1 // indirect
|
||||||
github.com/wyx2685/sing-vmess v0.0.0-20241213093025-7de21a950272 // indirect
|
github.com/wyx2685/sing-vmess v0.0.0-20250723121437-95d5ab59ff92 // indirect
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20240911120709-1fa0cb6f47c2 // indirect
|
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7 // indirect
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20240911121212-e4e74d0d02f5 // indirect
|
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect
|
||||||
|
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/ratelimit v0.3.0 // indirect
|
go.uber.org/ratelimit v0.3.0 // indirect
|
||||||
|
go.uber.org/zap/exp v0.3.0 // indirect
|
||||||
|
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // 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-20240719175910-8a7402abbf56 // indirect
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||||
golang.org/x/mod v0.21.0 // indirect
|
golang.org/x/mod v0.26.0 // indirect
|
||||||
golang.org/x/net v0.32.0 // indirect
|
golang.org/x/net v0.42.0 // indirect
|
||||||
golang.org/x/oauth2 v0.23.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/term v0.33.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
golang.org/x/tools v0.25.0 // indirect
|
golang.org/x/time v0.9.0 // indirect
|
||||||
|
golang.org/x/tools v0.34.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.197.0 // indirect
|
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
google.golang.org/api v0.214.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
|
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
|
||||||
google.golang.org/grpc v1.67.1 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||||
|
google.golang.org/grpc v1.74.2 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.12.0 // indirect
|
gopkg.in/ns1/ns1-go.v2 v2.13.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-20250428193742-2d800c3129d5 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
lukechampine.com/blake3 v1.4.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
//replace github.com/sagernet/sing-box v1.10.4 => /root/sing-box_mod
|
//replace github.com/sagernet/sing-box v1.12.0 => ../sing-box_mod
|
||||||
|
|
||||||
replace github.com/sagernet/sing-box v1.10.4 => github.com/wyx2685/sing-box_mod v0.0.0-20241213112838-c7faac61b3fe
|
replace github.com/sagernet/sing-box v1.12.0 => github.com/wyx2685/sing-box_mod v1.12.0
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package limiter
|
|
||||||
|
|
||||||
import log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
func ClearOnlineIP() error {
|
|
||||||
log.WithField("Type", "Limiter").
|
|
||||||
Debug("Clear online ip...")
|
|
||||||
limitLock.RLock()
|
|
||||||
for _, l := range limiter {
|
|
||||||
l.ConnLimiter.ClearOnlineIP()
|
|
||||||
}
|
|
||||||
limitLock.RUnlock()
|
|
||||||
log.WithField("Type", "Limiter").
|
|
||||||
Debug("Clear online ip done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
165
limiter/conn.go
165
limiter/conn.go
@@ -1,165 +0,0 @@
|
|||||||
package limiter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConnLimiter struct {
|
|
||||||
realtime bool
|
|
||||||
ipLimit int
|
|
||||||
connLimit int
|
|
||||||
count sync.Map // map[string]int
|
|
||||||
ip sync.Map // map[string]map[string]int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConnLimiter(conn int, ip int, realtime bool) *ConnLimiter {
|
|
||||||
return &ConnLimiter{
|
|
||||||
realtime: realtime,
|
|
||||||
connLimit: conn,
|
|
||||||
ipLimit: ip,
|
|
||||||
count: sync.Map{},
|
|
||||||
ip: sync.Map{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bool) {
|
|
||||||
if c.connLimit != 0 {
|
|
||||||
if v, ok := c.count.Load(user); ok {
|
|
||||||
if v.(int) >= c.connLimit {
|
|
||||||
// over connection limit
|
|
||||||
return true
|
|
||||||
} else if isTcp {
|
|
||||||
// tcp protocol
|
|
||||||
// connection count add
|
|
||||||
c.count.Store(user, v.(int)+1)
|
|
||||||
}
|
|
||||||
} else if isTcp {
|
|
||||||
// tcp protocol
|
|
||||||
// store connection count
|
|
||||||
c.count.Store(user, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.ipLimit == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// first user map
|
|
||||||
ipMap := new(sync.Map)
|
|
||||||
if c.realtime {
|
|
||||||
if isTcp {
|
|
||||||
ipMap.Store(ip, 2)
|
|
||||||
} else {
|
|
||||||
ipMap.Store(ip, 1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ipMap.Store(ip, time.Now())
|
|
||||||
}
|
|
||||||
// check user online ip
|
|
||||||
if v, ok := c.ip.LoadOrStore(user, ipMap); ok {
|
|
||||||
// have user
|
|
||||||
ips := v.(*sync.Map)
|
|
||||||
cn := 0
|
|
||||||
if online, ok := ips.Load(ip); ok {
|
|
||||||
// online ip
|
|
||||||
if c.realtime {
|
|
||||||
if isTcp {
|
|
||||||
// tcp count add
|
|
||||||
ips.Store(ip, online.(int)+2)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// update connect time for not realtime
|
|
||||||
ips.Store(ip, time.Now())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// not online ip
|
|
||||||
ips.Range(func(_, _ interface{}) bool {
|
|
||||||
cn++
|
|
||||||
if cn >= c.ipLimit {
|
|
||||||
limit = true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if limit {
|
|
||||||
// over ip limit
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.realtime {
|
|
||||||
if isTcp {
|
|
||||||
ips.Store(ip, 2)
|
|
||||||
} else {
|
|
||||||
ips.Store(ip, 1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ips.Store(ip, time.Now())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DelConnCount Delete tcp connection count, no tcp do not use
|
|
||||||
func (c *ConnLimiter) DelConnCount(user string, ip string) {
|
|
||||||
if !c.realtime {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.connLimit != 0 {
|
|
||||||
if v, ok := c.count.Load(user); ok {
|
|
||||||
if v.(int) == 1 {
|
|
||||||
c.count.Delete(user)
|
|
||||||
} else {
|
|
||||||
c.count.Store(user, v.(int)-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.ipLimit == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if i, ok := c.ip.Load(user); ok {
|
|
||||||
is := i.(*sync.Map)
|
|
||||||
if i, ok := is.Load(ip); ok {
|
|
||||||
if i.(int) == 2 {
|
|
||||||
is.Delete(ip)
|
|
||||||
} else {
|
|
||||||
is.Store(user, i.(int)-2)
|
|
||||||
}
|
|
||||||
notDel := false
|
|
||||||
c.ip.Range(func(_, _ any) bool {
|
|
||||||
notDel = true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if !notDel {
|
|
||||||
c.ip.Delete(user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearOnlineIP Clear udp,icmp and other packet protocol online ip
|
|
||||||
func (c *ConnLimiter) ClearOnlineIP() {
|
|
||||||
c.ip.Range(func(u, v any) bool {
|
|
||||||
userIp := v.(*sync.Map)
|
|
||||||
notDel := false
|
|
||||||
userIp.Range(func(ip, v any) bool {
|
|
||||||
notDel = true
|
|
||||||
if _, ok := v.(int); ok {
|
|
||||||
if v.(int) == 1 {
|
|
||||||
// clear packet ip for realtime
|
|
||||||
userIp.Delete(ip)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
// clear ip for not realtime
|
|
||||||
if v.(time.Time).Before(time.Now().Add(time.Minute)) {
|
|
||||||
// 1 minute no active
|
|
||||||
userIp.Delete(ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if !notDel {
|
|
||||||
c.ip.Delete(u)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package limiter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var c *ConnLimiter
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
c = NewConnLimiter(1, 1, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnLimiter_AddConnCount(t *testing.T) {
|
|
||||||
t.Log(c.AddConnCount("1", "1", true))
|
|
||||||
t.Log(c.AddConnCount("1", "2", true))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnLimiter_DelConnCount(t *testing.T) {
|
|
||||||
t.Log(c.AddConnCount("1", "1", true))
|
|
||||||
t.Log(c.AddConnCount("1", "2", true))
|
|
||||||
c.DelConnCount("1", "1")
|
|
||||||
t.Log(c.AddConnCount("1", "2", true))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnLimiter_ClearOnlineIP(t *testing.T) {
|
|
||||||
t.Log(c.AddConnCount("1", "1", false))
|
|
||||||
t.Log(c.AddConnCount("1", "2", false))
|
|
||||||
c.ClearOnlineIP()
|
|
||||||
t.Log(c.AddConnCount("1", "2", true))
|
|
||||||
c.DelConnCount("1", "2")
|
|
||||||
t.Log(c.AddConnCount("1", "1", false))
|
|
||||||
// not realtime
|
|
||||||
c.realtime = false
|
|
||||||
t.Log(c.AddConnCount("3", "2", true))
|
|
||||||
c.ClearOnlineIP()
|
|
||||||
t.Log(c.ip.Load("3"))
|
|
||||||
time.Sleep(time.Minute)
|
|
||||||
c.ClearOnlineIP()
|
|
||||||
t.Log(c.ip.Load("3"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkConnLimiter(b *testing.B) {
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
c.AddConnCount("1", "2", true)
|
|
||||||
c.DelConnCount("1", "2")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -11,8 +11,6 @@ import (
|
|||||||
"github.com/InazumaV/V2bX/common/format"
|
"github.com/InazumaV/V2bX/common/format"
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
"github.com/juju/ratelimit"
|
"github.com/juju/ratelimit"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/xtls/xray-core/common/task"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var limitLock sync.RWMutex
|
var limitLock sync.RWMutex
|
||||||
@@ -20,28 +18,17 @@ var limiter map[string]*Limiter
|
|||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
limiter = map[string]*Limiter{}
|
limiter = map[string]*Limiter{}
|
||||||
c := task.Periodic{
|
|
||||||
Interval: time.Minute * 3,
|
|
||||||
Execute: ClearOnlineIP,
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
log.WithField("Type", "Limiter").
|
|
||||||
Debug("ClearOnlineIP started")
|
|
||||||
time.Sleep(time.Minute * 3)
|
|
||||||
_ = c.Start()
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Limiter struct {
|
type Limiter struct {
|
||||||
DomainRules []*regexp.Regexp
|
DomainRules []*regexp.Regexp
|
||||||
ProtocolRules []string
|
ProtocolRules []string
|
||||||
SpeedLimit int
|
SpeedLimit int
|
||||||
UserOnlineIP *sync.Map // Key: Name, value: {Key: Ip, value: Uid}
|
UserOnlineIP *sync.Map // Key: TagUUID, value: {Key: Ip, value: Uid}
|
||||||
OldUserOnline *sync.Map // Key: Ip, value: Uid
|
OldUserOnline *sync.Map // Key: Ip, value: Uid
|
||||||
UUIDtoUID map[string]int // Key: UUID, value: Uid
|
UUIDtoUID map[string]int // Key: UUID, value: Uid
|
||||||
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
|
UserLimitInfo *sync.Map // Key: TagUUID value: UserLimitInfo
|
||||||
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
|
SpeedLimiter *sync.Map // key: TagUUID, value: *ratelimit.Bucket
|
||||||
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
|
|
||||||
AliveList map[int]int // Key: Uid, value: alive_ip
|
AliveList map[int]int // Key: Uid, value: alive_ip
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +46,6 @@ func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo, aliveLi
|
|||||||
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),
|
|
||||||
SpeedLimiter: new(sync.Map),
|
SpeedLimiter: new(sync.Map),
|
||||||
AliveList: aliveList,
|
AliveList: aliveList,
|
||||||
OldUserOnline: new(sync.Map),
|
OldUserOnline: new(sync.Map),
|
||||||
@@ -105,6 +91,7 @@ func (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, deleted []panel
|
|||||||
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))
|
l.UserOnlineIP.Delete(format.UserTag(tag, deleted[i].Uuid))
|
||||||
|
l.SpeedLimiter.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)
|
delete(l.AliveList, deleted[i].Id)
|
||||||
}
|
}
|
||||||
@@ -140,10 +127,6 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
|
|||||||
// check if ipv4 mapped ipv6
|
// check if ipv4 mapped ipv6
|
||||||
ip = strings.TrimPrefix(ip, "::ffff:")
|
ip = strings.TrimPrefix(ip, "::ffff:")
|
||||||
|
|
||||||
// ip and conn limiter
|
|
||||||
if l.ConnLimiter.AddConnCount(taguuid, ip, isTcp) {
|
|
||||||
return nil, true
|
|
||||||
}
|
|
||||||
// check and gen speed limit Bucket
|
// check and gen speed limit Bucket
|
||||||
nodeLimit := l.SpeedLimit
|
nodeLimit := l.SpeedLimit
|
||||||
userLimit := 0
|
userLimit := 0
|
||||||
@@ -164,25 +147,31 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
|
|||||||
} else {
|
} else {
|
||||||
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
|
userLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return nil, true
|
||||||
}
|
}
|
||||||
if noSSUDP {
|
if noSSUDP {
|
||||||
// Store online user for device limit
|
// Store online user for device limit
|
||||||
ipMap := new(sync.Map)
|
newipMap := new(sync.Map)
|
||||||
ipMap.Store(ip, uid)
|
newipMap.Store(ip, uid)
|
||||||
aliveIp := l.AliveList[uid]
|
aliveIp := l.AliveList[uid]
|
||||||
// If any device is online
|
// If any device is online
|
||||||
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
|
if v, loaded := l.UserOnlineIP.LoadOrStore(taguuid, newipMap); loaded {
|
||||||
ipMap := v.(*sync.Map)
|
oldipMap := v.(*sync.Map)
|
||||||
// If this is a new ip
|
// If this is a new ip
|
||||||
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
|
if _, loaded := oldipMap.LoadOrStore(ip, uid); !loaded {
|
||||||
if deviceLimit > 0 {
|
if v, loaded := l.OldUserOnline.Load(ip); loaded {
|
||||||
|
if v.(int) == uid {
|
||||||
|
l.OldUserOnline.Delete(ip)
|
||||||
|
}
|
||||||
|
} else if deviceLimit > 0 {
|
||||||
if deviceLimit <= aliveIp {
|
if deviceLimit <= aliveIp {
|
||||||
ipMap.Delete(ip)
|
oldipMap.Delete(ip)
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if v, ok := l.OldUserOnline.Load(ip); ok{
|
} else if v, ok := l.OldUserOnline.Load(ip); ok {
|
||||||
if v.(int) == uid {
|
if v.(int) == uid {
|
||||||
l.OldUserOnline.Delete(ip)
|
l.OldUserOnline.Delete(ip)
|
||||||
}
|
}
|
||||||
@@ -212,6 +201,7 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
|
|||||||
|
|
||||||
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
|
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
|
||||||
var onlineUser []panel.OnlineUser
|
var onlineUser []panel.OnlineUser
|
||||||
|
l.OldUserOnline = new(sync.Map)
|
||||||
l.UserOnlineIP.Range(func(key, value interface{}) bool {
|
l.UserOnlineIP.Range(func(key, value interface{}) bool {
|
||||||
taguuid := key.(string)
|
taguuid := key.(string)
|
||||||
ipMap := value.(*sync.Map)
|
ipMap := value.(*sync.Map)
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
"github.com/go-acme/lego/v4/challenge/http01"
|
"github.com/go-acme/lego/v4/challenge/http01"
|
||||||
"github.com/go-acme/lego/v4/providers/dns"
|
"github.com/go-acme/lego/v4/providers/dns"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
"github.com/goccy/go-json"
|
|
||||||
|
|
||||||
"github.com/InazumaV/V2bX/common/file"
|
"github.com/InazumaV/V2bX/common/file"
|
||||||
"github.com/InazumaV/V2bX/conf"
|
"github.com/InazumaV/V2bX/conf"
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"tag": c.tag,
|
"tag": c.tag,
|
||||||
"err": err,
|
"err": err,
|
||||||
}).Error("Delete node failed")
|
}).Panic("Delete node failed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"tag": c.tag,
|
"tag": c.tag,
|
||||||
"err": err,
|
"err": err,
|
||||||
}).Error("Add node failed")
|
}).Panic("Add node failed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_, err = c.server.AddUsers(&vCore.AddUsersParams{
|
_, err = c.server.AddUsers(&vCore.AddUsersParams{
|
||||||
@@ -178,7 +178,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
deleted, added := compareUserList(c.userList, newU)
|
deleted, added := compareUserList(c.userList, newU)
|
||||||
if len(deleted) > 0 {
|
if len(deleted) > 0 {
|
||||||
// have deleted users
|
// have deleted users
|
||||||
err = c.server.DelUsers(deleted, c.tag)
|
err = c.server.DelUsers(deleted, c.tag, c.info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"tag": c.tag,
|
"tag": c.tag,
|
||||||
|
|||||||
15
node/user.go
15
node/user.go
@@ -8,20 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Controller) reportUserTrafficTask() (err error) {
|
func (c *Controller) reportUserTrafficTask() (err error) {
|
||||||
// Get User traffic
|
userTraffic, err := c.server.GetUserTrafficSlice(c.tag, true)
|
||||||
userTraffic := make([]panel.UserTraffic, 0)
|
|
||||||
for i := range c.userList {
|
|
||||||
up, down := c.server.GetUserTraffic(c.tag, c.userList[i].Uuid, true)
|
|
||||||
if up > 0 || down > 0 {
|
|
||||||
if c.LimitConfig.EnableDynamicSpeedLimit {
|
|
||||||
c.traffic[c.userList[i].Uuid] += up + down
|
|
||||||
}
|
|
||||||
userTraffic = append(userTraffic, panel.UserTraffic{
|
|
||||||
UID: (c.userList)[i].Id,
|
|
||||||
Upload: up,
|
|
||||||
Download: down})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(userTraffic) > 0 {
|
if len(userTraffic) > 0 {
|
||||||
err = c.apiClient.ReportUserTraffic(userTraffic)
|
err = c.apiClient.ReportUserTraffic(userTraffic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user