mirror of
https://github.com/XrayR-project/XrayR.git
synced 2025-07-22 19:07:57 +00:00
Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0254c6c557 | ||
![]() |
4c52e33adb | ||
![]() |
b8d40c201b | ||
![]() |
dbd4a85a6c | ||
![]() |
8f28716b21 | ||
![]() |
73bc37cb51 | ||
![]() |
78f2f88296 | ||
![]() |
42a2226769 | ||
![]() |
71aba0601e | ||
![]() |
5fe18e020d | ||
![]() |
a0f2730bb2 | ||
![]() |
0c10c59877 | ||
![]() |
119e4810f2 | ||
![]() |
5dad910488 | ||
![]() |
8841e55f70 | ||
![]() |
5d20732881 | ||
![]() |
d579933451 | ||
![]() |
46e836f93b | ||
![]() |
e27b0c6cd8 | ||
![]() |
e34a3b4a94 | ||
![]() |
b366171401 | ||
![]() |
b034a2d48f | ||
![]() |
65b25ed3f6 | ||
![]() |
b65dfd7f92 | ||
![]() |
6569a0bf36 | ||
![]() |
e5e5e4ef92 | ||
![]() |
f35a056cc6 | ||
![]() |
73ffd3f505 | ||
![]() |
73c5e28f41 | ||
![]() |
f1b45c02f4 | ||
![]() |
941f256ba5 | ||
![]() |
2e547afdb7 | ||
![]() |
422ed1a311 | ||
![]() |
214e412993 | ||
![]() |
f85f6b47ee | ||
![]() |
7e09aef1cc | ||
![]() |
0b4caba8f6 | ||
![]() |
b0866011e5 | ||
![]() |
4d1d89b837 | ||
![]() |
0febf96021 | ||
![]() |
0f10e837e4 | ||
![]() |
5ab352f9c9 | ||
![]() |
5d5470a919 | ||
![]() |
db27722bbc | ||
![]() |
914510c687 | ||
![]() |
a8226b01e2 | ||
![]() |
54a958f39c | ||
![]() |
a1e407e18f | ||
![]() |
42da6c155d | ||
![]() |
89f2342a42 | ||
![]() |
ee1a606888 | ||
![]() |
c183c6492e | ||
![]() |
a70a0d9a31 | ||
![]() |
4c651e15fa | ||
![]() |
dda00c5dd6 | ||
![]() |
28e1b82320 | ||
![]() |
f1ab2eac13 | ||
![]() |
4f13aac094 | ||
![]() |
4ab196ad29 | ||
![]() |
9590697c29 | ||
![]() |
0bec6c4fdf | ||
![]() |
223589ba14 | ||
![]() |
6ba7fe2776 | ||
![]() |
b49798ab16 | ||
![]() |
af9224f5bb | ||
![]() |
c54650f195 | ||
![]() |
c73af2309b | ||
![]() |
169f742b76 | ||
![]() |
0fa8a45e51 | ||
![]() |
17c538a5b7 | ||
![]() |
5625d570fd | ||
![]() |
9b0e55f037 | ||
![]() |
4597d6ac56 | ||
![]() |
9aaad5e8ad | ||
![]() |
94d82f33ea | ||
![]() |
49aa520d67 | ||
![]() |
73c3047651 | ||
![]() |
613da96543 | ||
![]() |
76127f4757 | ||
![]() |
1d5a34cf98 | ||
![]() |
bf13971502 | ||
![]() |
572be38ae4 | ||
![]() |
34d96c9338 | ||
![]() |
4e69c0656e | ||
![]() |
88f7709fad |
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -46,7 +46,11 @@ jobs:
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
|
99
.github/workflows/docker.yml
vendored
99
.github/workflows/docker.yml
vendored
@@ -14,31 +14,92 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: xrayr-project/xrayr
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to Docker Hub
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm/v6
|
||||
- linux/arm/v7
|
||||
- linux/arm64
|
||||
- linux/s390x
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm/v7,linux/arm64,linux/amd64,linux/s390x
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: digests
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: digests
|
||||
path: /tmp/digests
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
ls -al
|
||||
echo docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# Build go
|
||||
FROM golang:1.20-alpine AS builder
|
||||
FROM golang:1.21-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
ENV CGO_ENABLED=0
|
||||
|
@@ -62,6 +62,7 @@ A Xray backend framework that can easily support many panels.
|
||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||
| [GoV2Panel](https://github.com/pingProMax/gov2panel) | √ | √ | √ |
|
||||
|
||||
## 软件安装
|
||||
|
||||
|
@@ -7,6 +7,12 @@ import (
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
)
|
||||
|
||||
const (
|
||||
UserNotModified = "users not modified"
|
||||
NodeNotModified = "node not modified"
|
||||
RuleNotModified = "rules not modified"
|
||||
)
|
||||
|
||||
// Config API config
|
||||
type Config struct {
|
||||
APIHost string `mapstructure:"ApiHost"`
|
||||
@@ -14,7 +20,7 @@ type Config struct {
|
||||
Key string `mapstructure:"ApiKey"`
|
||||
NodeType string `mapstructure:"NodeType"`
|
||||
EnableVless bool `mapstructure:"EnableVless"`
|
||||
EnableXTLS bool `mapstructure:"EnableXTLS"`
|
||||
VlessFlow string `mapstructure:"VlessFlow"`
|
||||
Timeout int `mapstructure:"Timeout"`
|
||||
SpeedLimit float64 `mapstructure:"SpeedLimit"`
|
||||
DeviceLimit int `mapstructure:"DeviceLimit"`
|
||||
@@ -41,29 +47,27 @@ type NodeInfo struct {
|
||||
Host string
|
||||
Path string
|
||||
EnableTLS bool
|
||||
TLSType string
|
||||
EnableVless bool
|
||||
VlessFlow string
|
||||
CypherMethod string
|
||||
ServerKey string
|
||||
ServiceName string
|
||||
Header json.RawMessage
|
||||
NameServerConfig []*conf.NameServerConfig
|
||||
EnableREALITY bool
|
||||
REALITYConfig *REALITYConfig
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
UID int
|
||||
Email string
|
||||
Passwd string
|
||||
Port uint32
|
||||
Method string
|
||||
SpeedLimit uint64 // Bps
|
||||
DeviceLimit int
|
||||
Protocol string
|
||||
ProtocolParam string
|
||||
Obfs string
|
||||
ObfsParam string
|
||||
UUID string
|
||||
AlterID uint16
|
||||
UID int
|
||||
Email string
|
||||
UUID string
|
||||
Passwd string
|
||||
Port uint32
|
||||
AlterID uint16
|
||||
Method string
|
||||
SpeedLimit uint64 // Bps
|
||||
DeviceLimit int
|
||||
}
|
||||
|
||||
type OnlineUser struct {
|
||||
@@ -94,3 +98,14 @@ type DetectResult struct {
|
||||
UID int
|
||||
RuleID int
|
||||
}
|
||||
|
||||
type REALITYConfig struct {
|
||||
Dest string
|
||||
ProxyProtocolVer uint64
|
||||
ServerNames []string
|
||||
PrivateKey string
|
||||
MinClientVer string
|
||||
MaxClientVer string
|
||||
MaxTimeDiff uint64
|
||||
ShortIds []string
|
||||
}
|
||||
|
402
api/gov2panel/gov2panel.go
Normal file
402
api/gov2panel/gov2panel.go
Normal file
@@ -0,0 +1,402 @@
|
||||
package gov2panel
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
// APIClient create an api client to the panel.
|
||||
type APIClient struct {
|
||||
client *resty.Client
|
||||
APIHost string
|
||||
NodeID int
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
VlessFlow string
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
LocalRuleList []api.DetectRule
|
||||
resp atomic.Value
|
||||
eTags map[string]string
|
||||
}
|
||||
|
||||
// New create an api instance
|
||||
func New(apiConfig *api.Config) *APIClient {
|
||||
client := resty.New()
|
||||
client.SetRetryCount(3)
|
||||
if apiConfig.Timeout > 0 {
|
||||
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
||||
} else {
|
||||
client.SetTimeout(5 * time.Second)
|
||||
}
|
||||
client.OnError(func(req *resty.Request, err error) {
|
||||
if v, ok := err.(*resty.ResponseError); ok {
|
||||
// v.Response contains the last response from the server
|
||||
// v.Err contains the original error
|
||||
log.Print(v.Err)
|
||||
}
|
||||
})
|
||||
client.SetBaseURL(apiConfig.APIHost)
|
||||
// Create Key for each requests
|
||||
client.SetQueryParams(map[string]string{
|
||||
"node_id": strconv.Itoa(apiConfig.NodeID),
|
||||
"node_type": strings.ToLower(apiConfig.NodeType),
|
||||
"token": apiConfig.Key,
|
||||
})
|
||||
// Read local rule list
|
||||
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
|
||||
apiClient := &APIClient{
|
||||
client: client,
|
||||
NodeID: apiConfig.NodeID,
|
||||
Key: apiConfig.Key,
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
VlessFlow: apiConfig.VlessFlow,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
eTags: make(map[string]string),
|
||||
}
|
||||
return apiClient
|
||||
}
|
||||
|
||||
// readLocalRuleList reads the local rule list file
|
||||
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
LocalRuleList = make([]api.DetectRule, 0)
|
||||
|
||||
if path != "" {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
defer file.Close()
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
}
|
||||
|
||||
fileScanner := bufio.NewScanner(file)
|
||||
|
||||
// read line by line
|
||||
for fileScanner.Scan() {
|
||||
LocalRuleList = append(LocalRuleList, api.DetectRule{
|
||||
ID: -1,
|
||||
Pattern: regexp.MustCompile(fileScanner.Text()),
|
||||
})
|
||||
}
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return LocalRuleList
|
||||
}
|
||||
|
||||
// Describe return a description of the client
|
||||
func (c *APIClient) Describe() api.ClientInfo {
|
||||
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
|
||||
}
|
||||
|
||||
// Debug set the client debug for client
|
||||
func (c *APIClient) Debug() {
|
||||
c.client.SetDebug(true)
|
||||
}
|
||||
|
||||
func (c *APIClient) assembleURL(path string) string {
|
||||
return c.APIHost + path
|
||||
}
|
||||
|
||||
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*simplejson.Json, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request %s failed: %v", c.assembleURL(path), err)
|
||||
}
|
||||
|
||||
if res.StatusCode() > 399 {
|
||||
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), res.String(), err)
|
||||
}
|
||||
|
||||
rtn, err := simplejson.NewJson(res.Body())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ret %s invalid", res.String())
|
||||
}
|
||||
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
// GetNodeInfo will pull NodeInfo Config from panel
|
||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
server := new(serverConfig)
|
||||
path := "/api/server/config"
|
||||
|
||||
res, err := c.client.R().
|
||||
SetHeader("If-None-Match", c.eTags["node"]).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.NodeNotModified)
|
||||
}
|
||||
// update etag
|
||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["node"] {
|
||||
c.eTags["node"] = res.Header().Get("Etag")
|
||||
}
|
||||
|
||||
nodeInfoResp, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, _ := nodeInfoResp.Encode()
|
||||
json.Unmarshal(b, server)
|
||||
|
||||
if gconv.Uint32(server.Port) == 0 {
|
||||
return nil, errors.New("server port must > 0")
|
||||
}
|
||||
|
||||
c.resp.Store(server)
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
nodeInfo, err = c.parseV2rayNodeResponse(server)
|
||||
case "Trojan":
|
||||
nodeInfo, err = c.parseTrojanNodeResponse(server)
|
||||
case "Shadowsocks":
|
||||
nodeInfo, err = c.parseSSNodeResponse(server)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse node info failed: %s, \nError: %v", res.String(), err)
|
||||
}
|
||||
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// GetUserList will pull user form panel
|
||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
var users []*user
|
||||
path := "/api/server/user"
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray", "Trojan", "Shadowsocks":
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
res, err := c.client.R().
|
||||
SetHeader("If-None-Match", c.eTags["users"]).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.UserNotModified)
|
||||
}
|
||||
// update etag
|
||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["users"] {
|
||||
c.eTags["users"] = res.Header().Get("Etag")
|
||||
}
|
||||
|
||||
usersResp, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, _ := usersResp.Get("users").Encode()
|
||||
json.Unmarshal(b, &users)
|
||||
if len(users) == 0 {
|
||||
return nil, errors.New("users is null")
|
||||
}
|
||||
|
||||
userList := make([]api.UserInfo, len(users))
|
||||
for i := 0; i < len(users); i++ {
|
||||
u := api.UserInfo{
|
||||
UID: users[i].Id,
|
||||
UUID: users[i].Uuid,
|
||||
}
|
||||
|
||||
// Support 1.7.1 speed limit
|
||||
if c.SpeedLimit > 0 {
|
||||
u.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
|
||||
} else {
|
||||
u.SpeedLimit = uint64(users[i].SpeedLimit * 1000000 / 8)
|
||||
}
|
||||
|
||||
u.DeviceLimit = c.DeviceLimit // todo waiting v2board send configuration
|
||||
u.Email = u.UUID + "@gov2panel.user"
|
||||
if c.NodeType == "Shadowsocks" {
|
||||
u.Passwd = u.UUID
|
||||
}
|
||||
userList[i] = u
|
||||
}
|
||||
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
// ReportUserTraffic reports the user traffic
|
||||
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
path := "/api/server/push"
|
||||
|
||||
res, err := c.client.R().SetBody(userTraffic).ForceContentType("application/json").Post(path)
|
||||
_, err = c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNodeRule implements the API interface
|
||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
routes := c.resp.Load().(*serverConfig).Routes
|
||||
|
||||
ruleList := c.LocalRuleList
|
||||
|
||||
for i := range routes {
|
||||
if routes[i].Action == "block" {
|
||||
|
||||
ruleList = append(ruleList, api.DetectRule{
|
||||
ID: i,
|
||||
Pattern: regexp.MustCompile(strings.Join(routes[i].Match, "|")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return &ruleList, nil
|
||||
}
|
||||
|
||||
// ReportNodeStatus implements the API interface
|
||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportNodeOnlineUsers implements the API interface
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportIllegal implements the API interface
|
||||
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseTrojanNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||
// Create GeneralNodeInfo
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: gconv.Uint32(s.Port),
|
||||
TransportProtocol: "tcp",
|
||||
EnableTLS: true,
|
||||
Host: s.Host,
|
||||
ServiceName: s.Sni,
|
||||
NameServerConfig: s.parseDNSConfig(),
|
||||
}
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// parseSSNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||
var header json.RawMessage
|
||||
|
||||
if s.Obfs == "http" {
|
||||
path := "/"
|
||||
if p := s.ObfsSettings.Path; p != "" {
|
||||
if strings.HasPrefix(p, "/") {
|
||||
path = p
|
||||
} else {
|
||||
path += p
|
||||
}
|
||||
}
|
||||
h := simplejson.New()
|
||||
h.Set("type", "http")
|
||||
h.SetPath([]string{"request", "path"}, path)
|
||||
header, _ = h.Encode()
|
||||
}
|
||||
// Create GeneralNodeInfo
|
||||
return &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: gconv.Uint32(s.Port),
|
||||
TransportProtocol: "tcp",
|
||||
CypherMethod: s.Encryption,
|
||||
ServerKey: s.ServerKey, // shadowsocks2022 share key
|
||||
NameServerConfig: s.parseDNSConfig(),
|
||||
Header: header,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// parseV2rayNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||
var (
|
||||
header json.RawMessage
|
||||
enableTLS bool
|
||||
)
|
||||
|
||||
switch s.Net {
|
||||
case "tcp":
|
||||
if s.Header != nil {
|
||||
if httpHeader, err := s.Header.MarshalJSON(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
header = httpHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.TLS == "tls" {
|
||||
enableTLS = true
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
return &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: gconv.Uint32(s.Port),
|
||||
AlterID: 0,
|
||||
TransportProtocol: s.Net,
|
||||
EnableTLS: enableTLS,
|
||||
Path: s.Path,
|
||||
Host: s.Host,
|
||||
EnableVless: c.EnableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
ServiceName: s.Sni,
|
||||
Header: header,
|
||||
NameServerConfig: s.parseDNSConfig(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *serverConfig) parseDNSConfig() (nameServerList []*conf.NameServerConfig) {
|
||||
for i := range s.Routes {
|
||||
if s.Routes[i].Action == "dns" {
|
||||
nameServerList = append(nameServerList, &conf.NameServerConfig{
|
||||
Address: &conf.Address{Address: net.ParseAddress(s.Routes[i].ActionValue)},
|
||||
Domains: s.Routes[i].Match,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@@ -1,24 +1,24 @@
|
||||
package v2board_test
|
||||
package gov2panel_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/api/v2board"
|
||||
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||
)
|
||||
|
||||
func CreateClient() api.API {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://localhost:9897",
|
||||
Key: "qwertyuiopasdfghjkl",
|
||||
APIHost: "http://localhost:8080",
|
||||
Key: "123456",
|
||||
NodeID: 1,
|
||||
NodeType: "V2ray",
|
||||
}
|
||||
client := v2board.New(apiConfig)
|
||||
client := gov2panel.New(apiConfig)
|
||||
return client
|
||||
}
|
||||
|
||||
func TestGetV2rayNodeinfo(t *testing.T) {
|
||||
func TestGetV2rayNodeInfo(t *testing.T) {
|
||||
client := CreateClient()
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
if err != nil {
|
||||
@@ -27,14 +27,14 @@ func TestGetV2rayNodeinfo(t *testing.T) {
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetSSNodeinfo(t *testing.T) {
|
||||
func TestGetSSNodeInfo(t *testing.T) {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://127.0.0.1:668",
|
||||
Key: "qwertyuiopasdfghjkl",
|
||||
NodeID: 1,
|
||||
NodeType: "Shadowsocks",
|
||||
}
|
||||
client := v2board.New(apiConfig)
|
||||
client := gov2panel.New(apiConfig)
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -42,14 +42,14 @@ func TestGetSSNodeinfo(t *testing.T) {
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetTrojanNodeinfo(t *testing.T) {
|
||||
func TestGetTrojanNodeInfo(t *testing.T) {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://127.0.0.1:668",
|
||||
Key: "qwertyuiopasdfghjkl",
|
||||
NodeID: 1,
|
||||
NodeType: "Trojan",
|
||||
}
|
||||
client := v2board.New(apiConfig)
|
||||
client := gov2panel.New(apiConfig)
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -78,8 +78,8 @@ func TestReportReportUserTraffic(t *testing.T) {
|
||||
for i, userInfo := range *userList {
|
||||
generalUserTraffic[i] = api.UserTraffic{
|
||||
UID: userInfo.UID,
|
||||
Upload: 114514,
|
||||
Download: 114514,
|
||||
Upload: 1111,
|
||||
Download: 2222,
|
||||
}
|
||||
}
|
||||
// client.Debug()
|
46
api/gov2panel/model.go
Normal file
46
api/gov2panel/model.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package gov2panel
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type serverConfig struct {
|
||||
v2ray
|
||||
shadowsocks
|
||||
//---
|
||||
Routes []route `json:"routes"`
|
||||
Header *json.RawMessage `json:"header"`
|
||||
}
|
||||
|
||||
type v2ray struct {
|
||||
Port string `json:"port"`
|
||||
Scy string `json:"scy"`
|
||||
Net string `json:"net"`
|
||||
Type string `json:"type"`
|
||||
Host string `json:"host"`
|
||||
Path string `json:"path"`
|
||||
TLS string `json:"tls"`
|
||||
Sni string `json:"sni"`
|
||||
Alpn string `json:"alpn"`
|
||||
}
|
||||
|
||||
type shadowsocks struct {
|
||||
Encryption string `json:"encryption"`
|
||||
Obfs string `json:"obfs"`
|
||||
ObfsSettings struct {
|
||||
Path string `json:"path"`
|
||||
Host string `json:"host"`
|
||||
} `json:"obfs_settings"`
|
||||
ServerKey string `json:"server_key"`
|
||||
}
|
||||
|
||||
type route struct {
|
||||
Id int `json:"id"`
|
||||
Match []string `json:"match"`
|
||||
Action string `json:"action"`
|
||||
ActionValue string `json:"action_value"`
|
||||
}
|
||||
|
||||
type user struct {
|
||||
Id int `json:"id"`
|
||||
Uuid string `json:"uuid"`
|
||||
SpeedLimit int `json:"speed_limit"`
|
||||
}
|
@@ -29,12 +29,12 @@ type APIClient struct {
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
EnableXTLS bool
|
||||
VlessFlow string
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
LocalRuleList []api.DetectRule
|
||||
resp atomic.Value
|
||||
eTag string
|
||||
eTags map[string]string
|
||||
}
|
||||
|
||||
// New create an api instance
|
||||
@@ -69,10 +69,11 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
EnableXTLS: apiConfig.EnableXTLS,
|
||||
VlessFlow: apiConfig.VlessFlow,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
eTags: make(map[string]string),
|
||||
}
|
||||
return apiClient
|
||||
}
|
||||
@@ -147,9 +148,19 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
path := "/api/v1/server/UniProxy/config"
|
||||
|
||||
res, err := c.client.R().
|
||||
SetHeader("If-None-Match", c.eTags["node"]).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.NodeNotModified)
|
||||
}
|
||||
// update etag
|
||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["node"] {
|
||||
c.eTags["node"] = res.Header().Get("Etag")
|
||||
}
|
||||
|
||||
nodeInfoResp, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -194,17 +205,17 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
}
|
||||
|
||||
res, err := c.client.R().
|
||||
SetHeader("If-None-Match", c.eTag).
|
||||
SetHeader("If-None-Match", c.eTags["users"]).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New("users no change")
|
||||
return nil, errors.New(api.UserNotModified)
|
||||
}
|
||||
// update etag
|
||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTag {
|
||||
c.eTag = res.Header().Get("Etag")
|
||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["users"] {
|
||||
c.eTags["users"] = res.Header().Get("Etag")
|
||||
}
|
||||
|
||||
usersResp, err := c.parseResponse(res, path, err)
|
||||
@@ -213,6 +224,9 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
}
|
||||
b, _ := usersResp.Get("users").Encode()
|
||||
json.Unmarshal(b, &users)
|
||||
if len(users) == 0 {
|
||||
return nil, errors.New("users is null")
|
||||
}
|
||||
|
||||
userList := make([]api.UserInfo, len(users))
|
||||
for i := 0; i < len(users); i++ {
|
||||
@@ -293,11 +307,6 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
|
||||
// parseTrojanNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||
var TLSType = "tls"
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
@@ -305,7 +314,6 @@ func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, err
|
||||
Port: uint32(s.ServerPort),
|
||||
TransportProtocol: "tcp",
|
||||
EnableTLS: true,
|
||||
TLSType: TLSType,
|
||||
Host: s.Host,
|
||||
ServiceName: s.ServerName,
|
||||
NameServerConfig: s.parseDNSConfig(),
|
||||
@@ -347,16 +355,11 @@ func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error)
|
||||
// parseV2rayNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||
var (
|
||||
TLSType = "tls"
|
||||
host string
|
||||
header json.RawMessage
|
||||
enableTLS bool
|
||||
)
|
||||
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
}
|
||||
|
||||
switch s.Network {
|
||||
case "ws":
|
||||
if s.NetworkSettings.Headers != nil {
|
||||
@@ -389,10 +392,10 @@ func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, erro
|
||||
AlterID: 0,
|
||||
TransportProtocol: s.Network,
|
||||
EnableTLS: enableTLS,
|
||||
TLSType: TLSType,
|
||||
Path: s.NetworkSettings.Path,
|
||||
Host: host,
|
||||
EnableVless: c.EnableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
ServiceName: s.NetworkSettings.ServiceName,
|
||||
Header: header,
|
||||
NameServerConfig: s.parseDNSConfig(),
|
||||
|
@@ -24,7 +24,7 @@ type APIClient struct {
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
EnableXTLS bool
|
||||
VlessFlow string
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
LocalRuleList []api.DetectRule
|
||||
@@ -61,7 +61,7 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
EnableXTLS: apiConfig.EnableXTLS,
|
||||
VlessFlow: apiConfig.VlessFlow,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
@@ -359,8 +359,8 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
var enableTLS bool
|
||||
var path, host, TLStype, transportProtocol, serviceName string
|
||||
var speedlimit uint64 = 0
|
||||
var path, host, transportProtocol, serviceName string
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
port := nodeInfoResponse.Port
|
||||
alterID := nodeInfoResponse.AlterId
|
||||
@@ -376,34 +376,29 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
}
|
||||
// Compatible with more node types config
|
||||
switch nodeInfoResponse.Security {
|
||||
case "tls", "xtls":
|
||||
if c.EnableXTLS {
|
||||
TLStype = "xtls"
|
||||
} else {
|
||||
TLStype = "tls"
|
||||
}
|
||||
case "tls":
|
||||
enableTLS = true
|
||||
default:
|
||||
enableTLS = false
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
AlterID: alterID,
|
||||
TransportProtocol: transportProtocol,
|
||||
EnableTLS: enableTLS,
|
||||
TLSType: TLStype,
|
||||
Path: path,
|
||||
Host: host,
|
||||
EnableVless: c.EnableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
ServiceName: serviceName,
|
||||
}
|
||||
|
||||
@@ -412,38 +407,33 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
|
||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: nodeInfoResponse.Port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
TransportProtocol: "tcp",
|
||||
CypherMethod: nodeInfoResponse.Method,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
// 域名或IP;port=连接端口#偏移端口|host=xx
|
||||
// gz.aaa.com;port=443#12345|host=hk.aaa.com
|
||||
var TLSType, host string
|
||||
var host string
|
||||
var transportProtocol = "tcp"
|
||||
var speedlimit uint64 = 0
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
} else {
|
||||
TLSType = "tls"
|
||||
}
|
||||
host = nodeInfoResponse.Host
|
||||
port := nodeInfoResponse.Port
|
||||
|
||||
@@ -456,25 +446,24 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
|
||||
transportProtocol = "grpc"
|
||||
}
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedlimit,
|
||||
TransportProtocol: transportProtocol,
|
||||
EnableTLS: true,
|
||||
TLSType: TLSType,
|
||||
Host: host,
|
||||
ServiceName: nodeInfoResponse.Sni,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseUserListResponse parse the response for the given nodeinfo format
|
||||
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
|
||||
var deviceLimit int = 0
|
||||
var speedlimit uint64 = 0
|
||||
var deviceLimit = 0
|
||||
var speedLimit uint64 = 0
|
||||
userList := make([]api.UserInfo, len(*userInfoResponse))
|
||||
for i, user := range *userInfoResponse {
|
||||
if c.DeviceLimit > 0 {
|
||||
@@ -483,15 +472,15 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
||||
deviceLimit = user.DeviceLimit
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
userList[i] = api.UserInfo{
|
||||
UID: user.ID,
|
||||
Passwd: user.Passwd,
|
||||
UUID: user.Passwd,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
DeviceLimit: deviceLimit,
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ type APIClient struct {
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
EnableXTLS bool
|
||||
VlessFlow string
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
LocalRuleList []api.DetectRule
|
||||
@@ -57,7 +57,7 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
EnableXTLS: apiConfig.EnableXTLS,
|
||||
VlessFlow: apiConfig.VlessFlow,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
@@ -412,13 +412,7 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
|
||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
||||
var TLStype string
|
||||
var speedlimit uint64 = 0
|
||||
if c.EnableXTLS {
|
||||
TLStype = "xtls"
|
||||
} else {
|
||||
TLStype = "tls"
|
||||
}
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
v2rayNodeInfo := new(V2rayNodeInfo)
|
||||
if err := json.Unmarshal(*nodeInfoResponse, v2rayNodeInfo); err != nil {
|
||||
@@ -426,9 +420,9 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((v2rayNodeInfo.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = (v2rayNodeInfo.SpeedLimit * 1000000) / 8
|
||||
}
|
||||
|
||||
if c.DeviceLimit == 0 && v2rayNodeInfo.ClientLimit > 0 {
|
||||
@@ -436,72 +430,65 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: v2rayNodeInfo.V2Port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
AlterID: v2rayNodeInfo.V2AlterID,
|
||||
TransportProtocol: v2rayNodeInfo.V2Net,
|
||||
FakeType: v2rayNodeInfo.V2Type,
|
||||
EnableTLS: v2rayNodeInfo.V2TLS,
|
||||
TLSType: TLStype,
|
||||
Path: v2rayNodeInfo.V2Path,
|
||||
Host: v2rayNodeInfo.V2Host,
|
||||
EnableVless: c.EnableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
shadowsocksNodeInfo := new(ShadowsocksNodeInfo)
|
||||
if err := json.Unmarshal(*nodeInfoResponse, shadowsocksNodeInfo); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
|
||||
if c.DeviceLimit == 0 && shadowsocksNodeInfo.ClientLimit > 0 {
|
||||
c.DeviceLimit = shadowsocksNodeInfo.ClientLimit
|
||||
}
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: shadowsocksNodeInfo.Port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
TransportProtocol: "tcp",
|
||||
CypherMethod: shadowsocksNodeInfo.Method,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
||||
|
||||
var TLSType string
|
||||
var speedlimit uint64 = 0
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
} else {
|
||||
TLSType = "tls"
|
||||
}
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
trojanNodeInfo := new(TrojanNodeInfo)
|
||||
if err := json.Unmarshal(*nodeInfoResponse, trojanNodeInfo); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((trojanNodeInfo.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = (trojanNodeInfo.SpeedLimit * 1000000) / 8
|
||||
}
|
||||
|
||||
if c.DeviceLimit == 0 && trojanNodeInfo.ClientLimit > 0 {
|
||||
@@ -509,22 +496,21 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: trojanNodeInfo.TrojanPort,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
TransportProtocol: "tcp",
|
||||
EnableTLS: true,
|
||||
TLSType: TLSType,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseV2rayUserListResponse parse the response for the given userinfo format
|
||||
func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
vmessUserList := new([]*VMessUser)
|
||||
if err := json.Unmarshal(*userInfoResponse, vmessUserList); err != nil {
|
||||
@@ -534,16 +520,16 @@ func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage
|
||||
userList := make([]api.UserInfo, len(*vmessUserList))
|
||||
for i, user := range *vmessUserList {
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = (user.SpeedLimit * 1000000) / 8
|
||||
}
|
||||
userList[i] = api.UserInfo{
|
||||
UID: user.UID,
|
||||
Email: "",
|
||||
UUID: user.VmessUID,
|
||||
DeviceLimit: c.DeviceLimit,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +538,7 @@ func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage
|
||||
|
||||
// ParseTrojanUserListResponse parse the response for the given userinfo format
|
||||
func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
trojanUserList := new([]*TrojanUser)
|
||||
if err := json.Unmarshal(*userInfoResponse, trojanUserList); err != nil {
|
||||
@@ -562,16 +548,16 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
|
||||
userList := make([]api.UserInfo, len(*trojanUserList))
|
||||
for i, user := range *trojanUserList {
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = (user.SpeedLimit * 1000000) / 8
|
||||
speedLimit = (user.SpeedLimit * 1000000) / 8
|
||||
}
|
||||
userList[i] = api.UserInfo{
|
||||
UID: user.UID,
|
||||
Email: "",
|
||||
UUID: user.Password,
|
||||
DeviceLimit: c.DeviceLimit,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +566,7 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
|
||||
|
||||
// ParseSSUserListResponse parse the response for the given userinfo format
|
||||
func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
ssUserList := new([]*SSUser)
|
||||
if err := json.Unmarshal(*userInfoResponse, ssUserList); err != nil {
|
||||
@@ -590,16 +576,16 @@ func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (
|
||||
userList := make([]api.UserInfo, len(*ssUserList))
|
||||
for i, user := range *ssUserList {
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64(user.SpeedLimit * 1000000 / 8)
|
||||
speedLimit = uint64(user.SpeedLimit * 1000000 / 8)
|
||||
}
|
||||
userList[i] = api.UserInfo{
|
||||
UID: user.UID,
|
||||
Email: "",
|
||||
Passwd: user.Password,
|
||||
DeviceLimit: c.DeviceLimit,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,6 @@ type NodeInfoResponse struct {
|
||||
Class int `json:"node_class"`
|
||||
SpeedLimit float64 `json:"node_speedlimit"`
|
||||
TrafficRate float64 `json:"traffic_rate"`
|
||||
MuOnly int `json:"mu_only"`
|
||||
Sort int `json:"sort"`
|
||||
RawServerString string `json:"server"`
|
||||
Type string `json:"type"`
|
||||
@@ -17,51 +16,35 @@ type NodeInfoResponse struct {
|
||||
}
|
||||
|
||||
type CustomConfig struct {
|
||||
OffsetPortUser string `json:"offset_port_user"`
|
||||
OffsetPortNode string `json:"offset_port_node"`
|
||||
ServerSub string `json:"server_sub"`
|
||||
Host string `json:"host"`
|
||||
MuPort string `json:"mu_port"`
|
||||
MuEncryption string `json:"mu_encryption"`
|
||||
MuProtocol string `json:"mu_protocol"`
|
||||
MuObfs string `json:"mu_obfs"`
|
||||
MuSuffix string `json:"mu_suffix"`
|
||||
V2Port string `json:"v2_port"`
|
||||
Method string `json:"method"`
|
||||
TLS string `json:"tls"`
|
||||
EnableVless string `json:"enable_vless"`
|
||||
AlterID string `json:"alter_id"`
|
||||
Network string `json:"network"`
|
||||
Security string `json:"security"`
|
||||
Path string `json:"path"`
|
||||
VerifyCert bool `json:"verify_cert"`
|
||||
Obfs string `json:"obfs"`
|
||||
Header json.RawMessage `json:"header"`
|
||||
TrojanPort string `json:"trojan_port"`
|
||||
AllowInsecure string `json:"allow_insecure"`
|
||||
Grpc string `json:"grpc"`
|
||||
Servicename string `json:"servicename"`
|
||||
EnableXtls string `json:"enable_xtls"`
|
||||
Flow string `json:"flow"`
|
||||
EnableREALITY bool `json:"enable_reality"`
|
||||
RealityOpts *REALITYConfig `json:"reality-opts"`
|
||||
}
|
||||
|
||||
// UserResponse is the response of user
|
||||
type UserResponse struct {
|
||||
ID int `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Passwd string `json:"passwd"`
|
||||
Port uint32 `json:"port"`
|
||||
Method string `json:"method"`
|
||||
SpeedLimit float64 `json:"node_speedlimit"`
|
||||
DeviceLimit int `json:"node_connector"`
|
||||
Protocol string `json:"protocol"`
|
||||
ProtocolParam string `json:"protocol_param"`
|
||||
Obfs string `json:"obfs"`
|
||||
ObfsParam string `json:"obfs_param"`
|
||||
ForbiddenIP string `json:"forbidden_ip"`
|
||||
ForbiddenPort string `json:"forbidden_port"`
|
||||
UUID string `json:"uuid"`
|
||||
MultiUser int `json:"is_multi_user"`
|
||||
AliveIP int `json:"alive_ip"`
|
||||
ID int `json:"id"`
|
||||
Passwd string `json:"passwd"`
|
||||
Port uint32 `json:"port"`
|
||||
Method string `json:"method"`
|
||||
SpeedLimit float64 `json:"node_speedlimit"`
|
||||
DeviceLimit int `json:"node_iplimit"`
|
||||
UUID string `json:"uuid"`
|
||||
AliveIP int `json:"alive_ip"`
|
||||
}
|
||||
|
||||
// Response is the common response
|
||||
@@ -75,7 +58,7 @@ type PostData struct {
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// SystemLoad is the data structure of systemload
|
||||
// SystemLoad is the data structure of system load
|
||||
type SystemLoad struct {
|
||||
Uptime string `json:"uptime"`
|
||||
Load string `json:"load"`
|
||||
@@ -103,3 +86,14 @@ type IllegalItem struct {
|
||||
ID int `json:"list_id"`
|
||||
UID int `json:"user_id"`
|
||||
}
|
||||
|
||||
type REALITYConfig struct {
|
||||
Dest string `json:"dest,omitempty"`
|
||||
ProxyProtocolVer uint64 `json:"proxy_protocol_ver,omitempty"`
|
||||
ServerNames []string `json:"server_names,omitempty"`
|
||||
PrivateKey string `json:"private_key,omitempty"`
|
||||
MinClientVer string `json:"min_client_ver,omitempty"`
|
||||
MaxClientVer string `json:"max_client_ver,omitempty"`
|
||||
MaxTimeDiff uint64 `json:"max_time_diff,omitempty"`
|
||||
ShortIds []string `json:"short_ids,omitempty"`
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package sspanel
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
@@ -32,19 +33,21 @@ type APIClient struct {
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
EnableXTLS bool
|
||||
VlessFlow string
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
DisableCustomConfig bool
|
||||
LocalRuleList []api.DetectRule
|
||||
LastReportOnline map[int]int
|
||||
access sync.Mutex
|
||||
version string
|
||||
eTags map[string]string
|
||||
}
|
||||
|
||||
// New creat a api instance
|
||||
// New create api instance
|
||||
func New(apiConfig *api.Config) *APIClient {
|
||||
|
||||
client := resty.New()
|
||||
|
||||
client.SetRetryCount(3)
|
||||
if apiConfig.Timeout > 0 {
|
||||
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
||||
@@ -52,12 +55,14 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
client.SetTimeout(5 * time.Second)
|
||||
}
|
||||
client.OnError(func(req *resty.Request, err error) {
|
||||
if v, ok := err.(*resty.ResponseError); ok {
|
||||
var v *resty.ResponseError
|
||||
if errors.As(err, &v) {
|
||||
// v.Response contains the last response from the server
|
||||
// v.Err contains the original error
|
||||
log.Print(v.Err)
|
||||
}
|
||||
})
|
||||
|
||||
client.SetBaseURL(apiConfig.APIHost)
|
||||
// Create Key for each requests
|
||||
client.SetQueryParam("key", apiConfig.Key)
|
||||
@@ -73,22 +78,23 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
EnableXTLS: apiConfig.EnableXTLS,
|
||||
VlessFlow: apiConfig.VlessFlow,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
DisableCustomConfig: apiConfig.DisableCustomConfig,
|
||||
LastReportOnline: make(map[int]int),
|
||||
eTags: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// readLocalRuleList reads the local rule list file
|
||||
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
|
||||
LocalRuleList = make([]api.DetectRule, 0)
|
||||
if path != "" {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
defer file.Close()
|
||||
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
@@ -110,8 +116,6 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
}
|
||||
|
||||
return LocalRuleList
|
||||
@@ -149,13 +153,22 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetNodeInfo will pull NodeInfo Config from sspanel
|
||||
// GetNodeInfo will pull NodeInfo Config from ssPanel
|
||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
|
||||
res, err := c.client.R().
|
||||
SetResult(&Response{}).
|
||||
SetHeader("If-None-Match", c.eTags["node"]).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.NodeNotModified)
|
||||
}
|
||||
|
||||
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["node"] {
|
||||
c.eTags["node"] = res.Header().Get("ETag")
|
||||
}
|
||||
|
||||
response, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
@@ -168,25 +181,18 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||
}
|
||||
|
||||
// New sspanel API
|
||||
disableCustomConfig := c.DisableCustomConfig
|
||||
if nodeInfoResponse.Version != "" && !disableCustomConfig {
|
||||
// Check if custom_config is empty
|
||||
if configString, err := json.Marshal(nodeInfoResponse.CustomConfig); err != nil || string(configString) == "[]" {
|
||||
log.Printf("custom_config is empty! take config from address now.")
|
||||
disableCustomConfig = true
|
||||
}
|
||||
} else {
|
||||
disableCustomConfig = true
|
||||
// determine ssPanel version, if disable custom config or version < 2021.11, then use old api
|
||||
c.version = nodeInfoResponse.Version
|
||||
var isExpired bool
|
||||
if compareVersion(c.version, "2021.11") == -1 {
|
||||
isExpired = true
|
||||
}
|
||||
|
||||
if !disableCustomConfig {
|
||||
nodeInfo, err = c.ParseSSPanelNodeInfo(nodeInfoResponse)
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(nodeInfoResponse)
|
||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
||||
if c.DisableCustomConfig || isExpired {
|
||||
if isExpired {
|
||||
log.Print("The panel version is expired, it is recommended to update immediately")
|
||||
}
|
||||
} else {
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
nodeInfo, err = c.ParseV2rayNodeResponse(nodeInfoResponse)
|
||||
@@ -199,6 +205,12 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
} else {
|
||||
nodeInfo, err = c.ParseSSPanelNodeInfo(nodeInfoResponse)
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(nodeInfoResponse)
|
||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -209,14 +221,23 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// GetUserList will pull user form sspanel
|
||||
// GetUserList will pull user form ssPanel
|
||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
path := "/mod_mu/users"
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||
SetHeader("If-None-Match", c.eTags["users"]).
|
||||
SetResult(&Response{}).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.UserNotModified)
|
||||
}
|
||||
|
||||
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["users"] {
|
||||
c.eTags["users"] = res.Header().Get("ETag")
|
||||
}
|
||||
|
||||
response, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
@@ -236,25 +257,27 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
return userList, nil
|
||||
}
|
||||
|
||||
// ReportNodeStatus reports the node status to the sspanel
|
||||
// ReportNodeStatus reports the node status to the ssPanel
|
||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
|
||||
systemload := SystemLoad{
|
||||
Uptime: strconv.FormatUint(nodeStatus.Uptime, 10),
|
||||
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.Mem/100, nodeStatus.Disk/100),
|
||||
// Determine whether a status report is in need
|
||||
if compareVersion(c.version, "2023.2") == -1 {
|
||||
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
|
||||
systemLoad := SystemLoad{
|
||||
Uptime: strconv.FormatUint(nodeStatus.Uptime, 10),
|
||||
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.Mem/100, nodeStatus.Disk/100),
|
||||
}
|
||||
|
||||
res, err := c.client.R().
|
||||
SetBody(systemLoad).
|
||||
SetResult(&Response{}).
|
||||
ForceContentType("application/json").
|
||||
Post(path)
|
||||
|
||||
_, err = c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
res, err := c.client.R().
|
||||
SetBody(systemload).
|
||||
SetResult(&Response{}).
|
||||
ForceContentType("application/json").
|
||||
Post(path)
|
||||
|
||||
_, err = c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -318,15 +341,25 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNodeRule will pull the audit rule form sspanel
|
||||
// GetNodeRule will pull the audit rule form ssPanel
|
||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
ruleList := c.LocalRuleList
|
||||
path := "/mod_mu/func/detect_rules"
|
||||
res, err := c.client.R().
|
||||
SetResult(&Response{}).
|
||||
SetHeader("If-None-Match", c.eTags["rules"]).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.RuleNotModified)
|
||||
}
|
||||
|
||||
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["rules"] {
|
||||
c.eTags["rules"] = res.Header().Get("ETag")
|
||||
}
|
||||
|
||||
response, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -372,12 +405,12 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
||||
// ParseV2rayNodeResponse parse the response for the given node info format
|
||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
var enableTLS bool
|
||||
var path, host, TLStype, transportProtocol, serviceName, HeaderType string
|
||||
var path, host, transportProtocol, serviceName, HeaderType string
|
||||
var header json.RawMessage
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
if nodeInfoResponse.RawServerString == "" {
|
||||
return nil, fmt.Errorf("no server info in response")
|
||||
}
|
||||
@@ -399,12 +432,7 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
// Compatible with more node types config
|
||||
for _, value := range serverConf[3:5] {
|
||||
switch value {
|
||||
case "tls", "xtls":
|
||||
if c.EnableXTLS {
|
||||
TLStype = "xtls"
|
||||
} else {
|
||||
TLStype = "tls"
|
||||
}
|
||||
case "tls":
|
||||
enableTLS = true
|
||||
default:
|
||||
if value != "" {
|
||||
@@ -429,14 +457,14 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
host = value
|
||||
case "servicename":
|
||||
serviceName = value
|
||||
case "headertype":
|
||||
case "headerType":
|
||||
HeaderType = value
|
||||
}
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
|
||||
if HeaderType != "" {
|
||||
@@ -449,29 +477,29 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
AlterID: alterID,
|
||||
TransportProtocol: transportProtocol,
|
||||
EnableTLS: enableTLS,
|
||||
TLSType: TLStype,
|
||||
Path: path,
|
||||
Host: host,
|
||||
EnableVless: c.EnableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
ServiceName: serviceName,
|
||||
Header: header,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
||||
// ParseSSNodeResponse parse the response for the given node info format
|
||||
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
var port uint32 = 0
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
var method string
|
||||
path := "/mod_mu/users"
|
||||
res, err := c.client.R().
|
||||
@@ -490,41 +518,30 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*ap
|
||||
if err := json.Unmarshal(response.Data, userListResponse); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
}
|
||||
// Find the multi-user
|
||||
for _, u := range *userListResponse {
|
||||
if u.MultiUser > 0 {
|
||||
port = u.Port
|
||||
method = u.Method
|
||||
break
|
||||
}
|
||||
}
|
||||
if port == 0 || method == "" {
|
||||
return nil, fmt.Errorf("cant find the single port multi user")
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
TransportProtocol: "tcp",
|
||||
CypherMethod: method,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseSSPluginNodeResponse parse the response for the given nodeinfor format
|
||||
// ParseSSPluginNodeResponse parse the response for the given node info format
|
||||
func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
var enableTLS bool
|
||||
var path, host, TLStype, transportProtocol string
|
||||
var speedlimit uint64 = 0
|
||||
var path, host, transportProtocol string
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
|
||||
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
|
||||
@@ -539,12 +556,7 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
|
||||
// Compatible with more node types config
|
||||
for _, value := range serverConf[3:5] {
|
||||
switch value {
|
||||
case "tls", "xtls":
|
||||
if c.EnableXTLS {
|
||||
TLStype = "xtls"
|
||||
} else {
|
||||
TLStype = "tls"
|
||||
}
|
||||
case "tls":
|
||||
enableTLS = true
|
||||
case "ws":
|
||||
transportProtocol = "ws"
|
||||
@@ -570,38 +582,32 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
|
||||
}
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
TransportProtocol: transportProtocol,
|
||||
EnableTLS: enableTLS,
|
||||
TLSType: TLStype,
|
||||
Path: path,
|
||||
Host: host,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
||||
// ParseTrojanNodeResponse parse the response for the given node info format
|
||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
// 域名或IP;port=连接端口#偏移端口|host=xx
|
||||
// gz.aaa.com;port=443#12345|host=hk.aaa.com
|
||||
var p, TLSType, host, outsidePort, insidePort, transportProtocol, serviceName string
|
||||
var speedlimit uint64 = 0
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
} else {
|
||||
TLSType = "tls"
|
||||
}
|
||||
var p, host, outsidePort, insidePort, transportProtocol, serviceName string
|
||||
var speedLimit uint64 = 0
|
||||
|
||||
if nodeInfoResponse.RawServerString == "" {
|
||||
return nil, fmt.Errorf("no server info in response")
|
||||
@@ -648,27 +654,26 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedlimit,
|
||||
SpeedLimit: speedLimit,
|
||||
TransportProtocol: transportProtocol,
|
||||
EnableTLS: true,
|
||||
TLSType: TLSType,
|
||||
Host: host,
|
||||
ServiceName: serviceName,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// ParseUserListResponse parse the response for the given nodeinfo format
|
||||
// ParseUserListResponse parse the response for the given node info format
|
||||
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
|
||||
c.access.Lock()
|
||||
// Clear Last report log
|
||||
@@ -678,7 +683,7 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
||||
}()
|
||||
|
||||
var deviceLimit, localDeviceLimit int = 0, 0
|
||||
var speedlimit uint64 = 0
|
||||
var speedLimit uint64 = 0
|
||||
var userList []api.UserInfo
|
||||
for _, user := range *userInfoResponse {
|
||||
if c.DeviceLimit > 0 {
|
||||
@@ -706,111 +711,147 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
userList = append(userList, api.UserInfo{
|
||||
UID: user.ID,
|
||||
Email: user.Email,
|
||||
UUID: user.UUID,
|
||||
Passwd: user.Passwd,
|
||||
SpeedLimit: speedlimit,
|
||||
DeviceLimit: deviceLimit,
|
||||
Port: user.Port,
|
||||
Method: user.Method,
|
||||
Protocol: user.Protocol,
|
||||
ProtocolParam: user.ProtocolParam,
|
||||
Obfs: user.Obfs,
|
||||
ObfsParam: user.ObfsParam,
|
||||
UID: user.ID,
|
||||
UUID: user.UUID,
|
||||
Passwd: user.Passwd,
|
||||
SpeedLimit: speedLimit,
|
||||
DeviceLimit: deviceLimit,
|
||||
Port: user.Port,
|
||||
Method: user.Method,
|
||||
})
|
||||
}
|
||||
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
// ParseSSPanelNodeInfo parse the response for the given nodeinfor format
|
||||
// Only used for SSPanel version >= 2021.11
|
||||
// ParseSSPanelNodeInfo parse the response for the given node info format
|
||||
// Only available for SSPanel version >= 2021.11
|
||||
func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
var (
|
||||
speedLimit uint64 = 0
|
||||
enableTLS, enableVless bool
|
||||
alterID uint16 = 0
|
||||
tlsType, transportProtocol string
|
||||
)
|
||||
|
||||
var speedlimit uint64 = 0
|
||||
var EnableTLS, EnableVless bool
|
||||
var AlterID uint16 = 0
|
||||
var TLSType, transportProtocol string
|
||||
// Check if custom_config is null
|
||||
if len(nodeInfoResponse.CustomConfig) == 0 {
|
||||
return nil, errors.New("custom_config is empty, disable custom config")
|
||||
}
|
||||
|
||||
nodeConfig := new(CustomConfig)
|
||||
json.Unmarshal(nodeInfoResponse.CustomConfig, nodeConfig)
|
||||
err := json.Unmarshal(nodeInfoResponse.CustomConfig, nodeConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("custom_config format error: %v", err)
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
|
||||
parsedPort, err := strconv.ParseInt(nodeConfig.OffsetPortNode, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port := uint32(parsedPort)
|
||||
|
||||
if c.NodeType == "Shadowsocks" {
|
||||
switch c.NodeType {
|
||||
case "Shadowsocks":
|
||||
transportProtocol = "tcp"
|
||||
}
|
||||
|
||||
if c.NodeType == "V2ray" {
|
||||
case "V2ray":
|
||||
transportProtocol = nodeConfig.Network
|
||||
TLSType = nodeConfig.Security
|
||||
if parsedAlterID, err := strconv.ParseInt(nodeConfig.AlterID, 10, 16); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
AlterID = uint16(parsedAlterID)
|
||||
tlsType = nodeConfig.Security
|
||||
|
||||
if tlsType == "tls" || tlsType == "xtls" {
|
||||
enableTLS = true
|
||||
}
|
||||
|
||||
if TLSType == "tls" || TLSType == "xtls" {
|
||||
EnableTLS = true
|
||||
}
|
||||
if nodeConfig.EnableVless == "1" {
|
||||
EnableVless = true
|
||||
enableVless = true
|
||||
}
|
||||
}
|
||||
|
||||
if c.NodeType == "Trojan" {
|
||||
EnableTLS = true
|
||||
TLSType = "tls"
|
||||
case "Trojan":
|
||||
enableTLS = true
|
||||
tlsType = "tls"
|
||||
transportProtocol = "tcp"
|
||||
|
||||
// Select security type
|
||||
if nodeConfig.EnableXtls == "1" {
|
||||
TLSType = "xtls"
|
||||
} else if nodeConfig.Security != "" {
|
||||
TLSType = nodeConfig.Security // try to read security from config
|
||||
if nodeConfig.Security != "" {
|
||||
tlsType = nodeConfig.Security // try to read security from config
|
||||
}
|
||||
|
||||
// Select transport protocol
|
||||
if nodeConfig.Grpc == "1" {
|
||||
transportProtocol = "grpc"
|
||||
} else if nodeConfig.Network != "" {
|
||||
if nodeConfig.Network != "" {
|
||||
transportProtocol = nodeConfig.Network // try to read transport protocol from config
|
||||
}
|
||||
}
|
||||
|
||||
// parse reality config
|
||||
realityConfig := new(api.REALITYConfig)
|
||||
if nodeConfig.RealityOpts != nil {
|
||||
r := nodeConfig.RealityOpts
|
||||
realityConfig = &api.REALITYConfig{
|
||||
Dest: r.Dest,
|
||||
ProxyProtocolVer: r.ProxyProtocolVer,
|
||||
ServerNames: r.ServerNames,
|
||||
PrivateKey: r.PrivateKey,
|
||||
MinClientVer: r.MinClientVer,
|
||||
MaxClientVer: r.MaxClientVer,
|
||||
MaxTimeDiff: r.MaxTimeDiff,
|
||||
ShortIds: r.ShortIds,
|
||||
}
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedlimit,
|
||||
AlterID: AlterID,
|
||||
SpeedLimit: speedLimit,
|
||||
AlterID: alterID,
|
||||
TransportProtocol: transportProtocol,
|
||||
Host: nodeConfig.Host,
|
||||
Path: nodeConfig.Path,
|
||||
EnableTLS: EnableTLS,
|
||||
TLSType: TLSType,
|
||||
EnableVless: EnableVless,
|
||||
CypherMethod: nodeConfig.MuEncryption,
|
||||
EnableTLS: enableTLS,
|
||||
EnableVless: enableVless,
|
||||
VlessFlow: nodeConfig.Flow,
|
||||
CypherMethod: nodeConfig.Method,
|
||||
ServiceName: nodeConfig.Servicename,
|
||||
Header: nodeConfig.Header,
|
||||
EnableREALITY: nodeConfig.EnableREALITY,
|
||||
REALITYConfig: realityConfig,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// compareVersion, version1 > version2 return 1, version1 < version2 return -1, 0 means equal
|
||||
func compareVersion(version1, version2 string) int {
|
||||
n, m := len(version1), len(version2)
|
||||
i, j := 0, 0
|
||||
for i < n || j < m {
|
||||
x := 0
|
||||
for ; i < n && version1[i] != '.'; i++ {
|
||||
x = x*10 + int(version1[i]-'0')
|
||||
}
|
||||
i++ // jump dot
|
||||
y := 0
|
||||
for ; j < m && version2[j] != '.'; j++ {
|
||||
y = y*10 + int(version2[j]-'0')
|
||||
}
|
||||
j++ // jump dot
|
||||
if x > y {
|
||||
return 1
|
||||
}
|
||||
if x < y {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ func CreateClient() api.API {
|
||||
return client
|
||||
}
|
||||
|
||||
func TestGetV2rayNodeinfo(t *testing.T) {
|
||||
func TestGetV2rayNodeInfo(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
@@ -29,7 +29,7 @@ func TestGetV2rayNodeinfo(t *testing.T) {
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetSSNodeinfo(t *testing.T) {
|
||||
func TestGetSSNodeInfo(t *testing.T) {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://127.0.0.1:667",
|
||||
Key: "123",
|
||||
@@ -44,7 +44,7 @@ func TestGetSSNodeinfo(t *testing.T) {
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetTrojanNodeinfo(t *testing.T) {
|
||||
func TestGetTrojanNodeInfo(t *testing.T) {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://127.0.0.1:667",
|
||||
Key: "123",
|
||||
@@ -59,7 +59,7 @@ func TestGetTrojanNodeinfo(t *testing.T) {
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetSSinfo(t *testing.T) {
|
||||
func TestGetSSInfo(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
|
@@ -1,8 +0,0 @@
|
||||
// Deprecated: after 2023.6.1
|
||||
package v2board
|
||||
|
||||
type UserTraffic struct {
|
||||
UID int `json:"user_id"`
|
||||
Upload int64 `json:"u"`
|
||||
Download int64 `json:"d"`
|
||||
}
|
@@ -1,425 +0,0 @@
|
||||
package v2board
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
// APIClient create an api client to the panel.
|
||||
type APIClient struct {
|
||||
client *resty.Client
|
||||
APIHost string
|
||||
NodeID int
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
EnableXTLS bool
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
LocalRuleList []api.DetectRule
|
||||
ConfigResp *simplejson.Json
|
||||
access sync.Mutex
|
||||
}
|
||||
|
||||
// New create an api instance
|
||||
func New(apiConfig *api.Config) *APIClient {
|
||||
|
||||
client := resty.New()
|
||||
client.SetRetryCount(3)
|
||||
if apiConfig.Timeout > 0 {
|
||||
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
||||
} else {
|
||||
client.SetTimeout(5 * time.Second)
|
||||
}
|
||||
client.OnError(func(req *resty.Request, err error) {
|
||||
if v, ok := err.(*resty.ResponseError); ok {
|
||||
// v.Response contains the last response from the server
|
||||
// v.Err contains the original error
|
||||
log.Print(v.Err)
|
||||
}
|
||||
})
|
||||
client.SetBaseURL(apiConfig.APIHost)
|
||||
// Create Key for each requests
|
||||
client.SetQueryParams(map[string]string{
|
||||
"node_id": strconv.Itoa(apiConfig.NodeID),
|
||||
"token": apiConfig.Key,
|
||||
})
|
||||
// Read local rule list
|
||||
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
|
||||
apiClient := &APIClient{
|
||||
client: client,
|
||||
NodeID: apiConfig.NodeID,
|
||||
Key: apiConfig.Key,
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
EnableXTLS: apiConfig.EnableXTLS,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
}
|
||||
return apiClient
|
||||
}
|
||||
|
||||
// readLocalRuleList reads the local rule list file
|
||||
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
|
||||
LocalRuleList = make([]api.DetectRule, 0)
|
||||
if path != "" {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
}
|
||||
|
||||
fileScanner := bufio.NewScanner(file)
|
||||
|
||||
// read line by line
|
||||
for fileScanner.Scan() {
|
||||
LocalRuleList = append(LocalRuleList, api.DetectRule{
|
||||
ID: -1,
|
||||
Pattern: regexp.MustCompile(fileScanner.Text()),
|
||||
})
|
||||
}
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
}
|
||||
|
||||
return LocalRuleList
|
||||
}
|
||||
|
||||
// Describe return a description of the client
|
||||
func (c *APIClient) Describe() api.ClientInfo {
|
||||
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
|
||||
}
|
||||
|
||||
// Debug set the client debug for client
|
||||
func (c *APIClient) Debug() {
|
||||
c.client.SetDebug(true)
|
||||
}
|
||||
|
||||
func (c *APIClient) assembleURL(path string) string {
|
||||
return c.APIHost + path
|
||||
}
|
||||
|
||||
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*simplejson.Json, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request %s failed: %s", c.assembleURL(path), err)
|
||||
}
|
||||
|
||||
if res.StatusCode() > 400 {
|
||||
body := res.Body()
|
||||
return nil, fmt.Errorf("request %s failed: %s, %s", c.assembleURL(path), string(body), err)
|
||||
}
|
||||
rtn, err := simplejson.NewJson(res.Body())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ret %s invalid", res.String())
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
// GetNodeInfo will pull NodeInfo Config from sspanel
|
||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
path = "/api/v1/server/Deepbwork/config"
|
||||
case "Trojan":
|
||||
path = "/api/v1/server/TrojanTidalab/config"
|
||||
case "Shadowsocks":
|
||||
if nodeInfo, err = c.ParseSSNodeResponse(); err == nil {
|
||||
return nodeInfo, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("local_port", "1").
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
|
||||
response, err := c.parseResponse(res, path, err)
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
c.ConfigResp = response
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
nodeInfo, err = c.ParseV2rayNodeResponse(response)
|
||||
case "Trojan":
|
||||
nodeInfo, err = c.ParseTrojanNodeResponse(response)
|
||||
case "Shadowsocks":
|
||||
nodeInfo, err = c.ParseSSNodeResponse()
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
res, _ := response.MarshalJSON()
|
||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s", string(res), err)
|
||||
}
|
||||
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
// GetUserList will pull user form sspanel
|
||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
path = "/api/v1/server/Deepbwork/user"
|
||||
case "Trojan":
|
||||
path = "/api/v1/server/TrojanTidalab/user"
|
||||
case "Shadowsocks":
|
||||
path = "/api/v1/server/ShadowsocksTidalab/user"
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
res, err := c.client.R().
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
|
||||
response, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numOfUsers := len(response.Get("data").MustArray())
|
||||
userList := make([]api.UserInfo, numOfUsers)
|
||||
for i := 0; i < numOfUsers; i++ {
|
||||
user := api.UserInfo{}
|
||||
user.UID = response.Get("data").GetIndex(i).Get("id").MustInt()
|
||||
user.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
|
||||
user.DeviceLimit = c.DeviceLimit
|
||||
switch c.NodeType {
|
||||
case "Shadowsocks":
|
||||
user.Email = response.Get("data").GetIndex(i).Get("secret").MustString()
|
||||
user.Passwd = response.Get("data").GetIndex(i).Get("secret").MustString()
|
||||
user.Method = response.Get("data").GetIndex(i).Get("cipher").MustString()
|
||||
user.Port = uint32(response.Get("data").GetIndex(i).Get("port").MustUint64())
|
||||
case "Trojan":
|
||||
user.UUID = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
||||
user.Email = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
||||
case "V2ray":
|
||||
user.UUID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("uuid").MustString()
|
||||
user.Email = response.Get("data").GetIndex(i).Get("v2ray_user").Get("email").MustString()
|
||||
user.AlterID = uint16(response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustUint64())
|
||||
}
|
||||
userList[i] = user
|
||||
}
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
// ReportUserTraffic reports the user traffic
|
||||
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
path = "/api/v1/server/Deepbwork/submit"
|
||||
case "Trojan":
|
||||
path = "/api/v1/server/TrojanTidalab/submit"
|
||||
case "Shadowsocks":
|
||||
path = "/api/v1/server/ShadowsocksTidalab/submit"
|
||||
}
|
||||
|
||||
data := make([]UserTraffic, len(*userTraffic))
|
||||
for i, traffic := range *userTraffic {
|
||||
data[i] = UserTraffic{
|
||||
UID: traffic.UID,
|
||||
Upload: traffic.Upload,
|
||||
Download: traffic.Download}
|
||||
}
|
||||
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||
SetBody(data).
|
||||
ForceContentType("application/json").
|
||||
Post(path)
|
||||
_, err = c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNodeRule implements the API interface
|
||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
ruleList := c.LocalRuleList
|
||||
if c.NodeType != "V2ray" {
|
||||
return &ruleList, nil
|
||||
}
|
||||
|
||||
// V2board only support the rule for v2ray
|
||||
// fix: reuse config response
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
ruleListResponse := c.ConfigResp.Get("routing").Get("rules").GetIndex(1).Get("domain").MustStringArray()
|
||||
for i, rule := range ruleListResponse {
|
||||
rule = strings.TrimPrefix(rule, "regexp:")
|
||||
ruleListItem := api.DetectRule{
|
||||
ID: i,
|
||||
Pattern: regexp.MustCompile(rule),
|
||||
}
|
||||
ruleList = append(ruleList, ruleListItem)
|
||||
}
|
||||
return &ruleList, nil
|
||||
}
|
||||
|
||||
// ReportNodeStatus implements the API interface
|
||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportNodeOnlineUsers implements the API interface
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportIllegal implements the API interface
|
||||
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
||||
var TLSType = "tls"
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
}
|
||||
port := uint32(nodeInfoResponse.Get("local_port").MustUint64())
|
||||
host := nodeInfoResponse.Get("ssl").Get("sni").MustString()
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
TransportProtocol: "tcp",
|
||||
EnableTLS: true,
|
||||
TLSType: TLSType,
|
||||
Host: host,
|
||||
}
|
||||
return nodeinfo, nil
|
||||
}
|
||||
|
||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseSSNodeResponse() (*api.NodeInfo, error) {
|
||||
var port uint32
|
||||
var method string
|
||||
userInfo, err := c.GetUserList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(*userInfo) > 0 {
|
||||
port = (*userInfo)[0].Port
|
||||
method = (*userInfo)[0].Method
|
||||
} else {
|
||||
return nil, errors.New("the number of node users is 0")
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeinfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
TransportProtocol: "tcp",
|
||||
CypherMethod: method,
|
||||
}
|
||||
|
||||
return nodeinfo, nil
|
||||
}
|
||||
|
||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
||||
var TLSType string = "tls"
|
||||
var path, host, serviceName string
|
||||
var header json.RawMessage
|
||||
var enableTLS bool
|
||||
var alterID uint16 = 0
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
}
|
||||
|
||||
inboundInfo := simplejson.New()
|
||||
if tmpInboundInfo, ok := nodeInfoResponse.CheckGet("inbound"); ok {
|
||||
inboundInfo = tmpInboundInfo
|
||||
// Compatible with v2board 1.5.5-dev
|
||||
} else if tmpInboundInfo, ok := nodeInfoResponse.CheckGet("inbounds"); ok {
|
||||
tmpInboundInfo := tmpInboundInfo.MustArray()
|
||||
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
||||
inboundInfo, _ = simplejson.NewJson(marshalByte)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unable to find inbound(s) in the nodeInfo")
|
||||
}
|
||||
|
||||
port := uint32(inboundInfo.Get("port").MustUint64())
|
||||
transportProtocol := inboundInfo.Get("streamSettings").Get("network").MustString()
|
||||
|
||||
switch transportProtocol {
|
||||
case "ws":
|
||||
path = inboundInfo.Get("streamSettings").Get("wsSettings").Get("path").MustString()
|
||||
host = inboundInfo.Get("streamSettings").Get("wsSettings").Get("headers").Get("Host").MustString()
|
||||
case "grpc":
|
||||
if data, ok := inboundInfo.Get("streamSettings").Get("grpcSettings").CheckGet("serviceName"); ok {
|
||||
serviceName = data.MustString()
|
||||
}
|
||||
case "tcp":
|
||||
if data, ok := inboundInfo.Get("streamSettings").Get("tcpSettings").CheckGet("header"); ok {
|
||||
if httpHeader, err := data.MarshalJSON(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
header = httpHeader
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if inboundInfo.Get("streamSettings").Get("security").MustString() == "tls" {
|
||||
enableTLS = true
|
||||
} else {
|
||||
enableTLS = false
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
// AlterID will be updated after next sync
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
AlterID: alterID,
|
||||
TransportProtocol: transportProtocol,
|
||||
EnableTLS: enableTLS,
|
||||
TLSType: TLSType,
|
||||
Path: path,
|
||||
Host: host,
|
||||
EnableVless: c.EnableVless,
|
||||
ServiceName: serviceName,
|
||||
Header: header,
|
||||
}
|
||||
return nodeInfo, nil
|
||||
}
|
@@ -28,7 +28,7 @@ type APIClient struct {
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
EnableXTLS bool
|
||||
VlessFlow string
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
LocalRuleList []api.DetectRule
|
||||
@@ -67,7 +67,7 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
EnableXTLS: apiConfig.EnableXTLS,
|
||||
VlessFlow: apiConfig.VlessFlow,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
@@ -302,11 +302,6 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
|
||||
// ParseTrojanNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
||||
var TLSType = "tls"
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
}
|
||||
|
||||
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
|
||||
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
||||
inboundInfo, _ := simplejson.NewJson(marshalByte)
|
||||
@@ -321,7 +316,6 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (
|
||||
Port: port,
|
||||
TransportProtocol: "tcp",
|
||||
EnableTLS: true,
|
||||
TLSType: TLSType,
|
||||
Host: host,
|
||||
}
|
||||
return nodeInfo, nil
|
||||
@@ -364,14 +358,10 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api
|
||||
|
||||
// ParseV2rayNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
||||
var TLSType = "tls"
|
||||
var path, host, serviceName string
|
||||
var header json.RawMessage
|
||||
var enableTLS bool
|
||||
var alterID uint16 = 0
|
||||
if c.EnableXTLS {
|
||||
TLSType = "xtls"
|
||||
}
|
||||
|
||||
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
|
||||
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
||||
@@ -413,10 +403,10 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
||||
AlterID: alterID,
|
||||
TransportProtocol: transportProtocol,
|
||||
EnableTLS: enableTLS,
|
||||
TLSType: TLSType,
|
||||
Path: path,
|
||||
Host: host,
|
||||
EnableVless: c.EnableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
ServiceName: serviceName,
|
||||
Header: header,
|
||||
}
|
||||
|
@@ -145,76 +145,9 @@ func (*DefaultDispatcher) Close() error {
|
||||
}
|
||||
|
||||
func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link, error) {
|
||||
downOpt := pipe.OptionsFromContext(ctx)
|
||||
upOpt := downOpt
|
||||
|
||||
if network == net.Network_UDP {
|
||||
var ip2domain *sync.Map // net.IP.String() => domain, this map is used by server side when client turn on fakedns
|
||||
// Client will send domain address in the buffer.UDP.Address, server record all possible target IP addrs.
|
||||
// When target replies, server will restore the domain and send back to client.
|
||||
// Note: this map is not global but per connection context
|
||||
upOpt = append(upOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
||||
for i, buffer := range mb {
|
||||
if buffer.UDP == nil {
|
||||
continue
|
||||
}
|
||||
addr := buffer.UDP.Address
|
||||
if addr.Family().IsIP() {
|
||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(addr) && sniffing.Enabled {
|
||||
domain := fkr0.GetDomainFromFakeDNS(addr)
|
||||
if len(domain) > 0 {
|
||||
buffer.UDP.Address = net.DomainAddress(domain)
|
||||
newError("[fakedns client] override with domain: ", domain, " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
} else {
|
||||
newError("[fakedns client] failed to find domain! :", addr.String(), " for xUDP buffer at ", i).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ip2domain == nil {
|
||||
ip2domain = new(sync.Map)
|
||||
newError("[fakedns client] create a new map").WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
domain := addr.Domain()
|
||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
|
||||
if err == nil {
|
||||
for _, ip := range ips {
|
||||
ip2domain.Store(ip.String(), domain)
|
||||
}
|
||||
newError("[fakedns client] candidate ip: "+fmt.Sprintf("%v", ips), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
} else {
|
||||
newError("[fakedns client] failed to look up IP for ", domain, " for xUDP buffer at ", i).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
}
|
||||
}
|
||||
return mb
|
||||
}))
|
||||
downOpt = append(downOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
||||
for i, buffer := range mb {
|
||||
if buffer.UDP == nil {
|
||||
continue
|
||||
}
|
||||
addr := buffer.UDP.Address
|
||||
if addr.Family().IsIP() {
|
||||
if ip2domain == nil {
|
||||
continue
|
||||
}
|
||||
if domain, found := ip2domain.Load(addr.IP().String()); found {
|
||||
buffer.UDP.Address = net.DomainAddress(domain.(string))
|
||||
newError("[fakedns client] restore domain: ", domain.(string), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
} else {
|
||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok {
|
||||
fakeIp := fkr0.GetFakeIPForDomain(addr.Domain())
|
||||
buffer.UDP.Address = fakeIp[0]
|
||||
newError("[fakedns client] restore FakeIP: ", buffer.UDP, fmt.Sprintf("%v", fakeIp), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
}
|
||||
}
|
||||
return mb
|
||||
}))
|
||||
}
|
||||
uplinkReader, uplinkWriter := pipe.New(upOpt...)
|
||||
downlinkReader, downlinkWriter := pipe.New(downOpt...)
|
||||
opt := pipe.OptionsFromContext(ctx)
|
||||
uplinkReader, uplinkWriter := pipe.New(opt...)
|
||||
downlinkReader, downlinkWriter := pipe.New(opt...)
|
||||
|
||||
inboundLink := &transport.Link{
|
||||
Reader: downlinkReader,
|
||||
@@ -247,6 +180,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sn
|
||||
inboundLink.Writer = d.Limiter.RateWriter(inboundLink.Writer, bucket)
|
||||
outboundLink.Writer = d.Limiter.RateWriter(outboundLink.Writer, bucket)
|
||||
}
|
||||
|
||||
p := d.policy.ForLevel(user.Level)
|
||||
if p.Stats.UserUplink {
|
||||
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
||||
|
@@ -14,8 +14,8 @@ import (
|
||||
"github.com/eko/gocache/lib/v4/store"
|
||||
goCacheStore "github.com/eko/gocache/store/go_cache/v4"
|
||||
redisStore "github.com/eko/gocache/store/redis/v4"
|
||||
"github.com/go-redis/redis/v8"
|
||||
goCache "github.com/patrickmn/go-cache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
|
221
go.mod
221
go.mod
@@ -1,193 +1,232 @@
|
||||
module github.com/XrayR-project/XrayR
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.1
|
||||
|
||||
require (
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
dario.cat/mergo v1.0.0
|
||||
github.com/bitly/go-simplejson v0.5.1
|
||||
github.com/deckarep/golang-set v1.8.0
|
||||
github.com/eko/gocache/lib/v4 v4.1.2
|
||||
github.com/eko/gocache/store/go_cache/v4 v4.1.2
|
||||
github.com/eko/gocache/store/redis/v4 v4.1.2
|
||||
github.com/eko/gocache/lib/v4 v4.1.5
|
||||
github.com/eko/gocache/store/go_cache/v4 v4.2.1
|
||||
github.com/eko/gocache/store/redis/v4 v4.2.1
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-acme/lego/v4 v4.9.2-0.20221210101705-6695fcc87344
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/imdario/mergo v0.3.13
|
||||
github.com/go-acme/lego/v4 v4.14.2
|
||||
github.com/go-resty/resty/v2 v2.9.1
|
||||
github.com/gogf/gf/v2 v2.5.4
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/r3labs/diff/v2 v2.15.1
|
||||
github.com/sagernet/sing v0.1.7
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
|
||||
github.com/shirou/gopsutil/v3 v3.23.1
|
||||
github.com/spf13/viper v1.15.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/xtls/xray-core v1.7.5
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/net v0.7.0
|
||||
github.com/redis/go-redis/v9 v9.2.1
|
||||
github.com/sagernet/sing v0.2.13
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5
|
||||
github.com/shirou/gopsutil/v3 v3.23.9
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/xtls/xray-core v1.8.4
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/net v0.16.0
|
||||
golang.org/x/time v0.3.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
google.golang.org/protobuf v1.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.15.1 // indirect
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // 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/sdk/azcore v1.6.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.19.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.28 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.27.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.28.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/civo/civogo v0.3.11 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.49.0 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
|
||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/dnsimple/dnsimple-go v0.71.1 // indirect
|
||||
github.com/exoscale/egoscale v0.93.0 // indirect
|
||||
github.com/dnsimple/dnsimple-go v1.2.0 // indirect
|
||||
github.com/exoscale/egoscale v0.100.1 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/gaukas/godicttls v0.0.4 // indirect
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gofrs/uuid v4.3.1+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||
github.com/linode/linodego v1.9.1 // indirect
|
||||
github.com/linode/linodego v1.17.2 // indirect
|
||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/mimuret/golang-iij-dpf v0.7.1 // indirect
|
||||
github.com/miekg/dns v1.1.55 // indirect
|
||||
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.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/reflect2 v1.0.2 // indirect
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/desec v0.6.0 // indirect
|
||||
github.com/nrdcg/bunny-go v0.0.0-20230728143221-c9dda82568d9 // indirect
|
||||
github.com/nrdcg/desec v0.7.0 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.2.0 // indirect
|
||||
github.com/nrdcg/goinwx v0.8.1 // indirect
|
||||
github.com/nrdcg/goinwx v0.8.2 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/nrdcg/porkbun v0.1.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.8.0 // indirect
|
||||
github.com/nrdcg/nodion v0.1.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.2.0 // indirect
|
||||
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
||||
github.com/ovh/go-ovh v1.1.0 // indirect
|
||||
github.com/ovh/go-ovh v1.4.2 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/pquerna/otp v1.3.0 // indirect
|
||||
github.com/pquerna/otp v1.4.0 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/quic-go/quic-go v0.32.0 // indirect
|
||||
github.com/refraction-networking/utls v1.2.2-0.20230207151345-a75a4b484849 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||
github.com/quic-go/quic-go v0.38.1 // indirect
|
||||
github.com/refraction-networking/utls v1.4.3 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.1 // indirect
|
||||
github.com/sacloud/go-http v0.1.2 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.3.2 // indirect
|
||||
github.com/sacloud/packages-go v0.0.5 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.8 // indirect
|
||||
github.com/sacloud/go-http v0.1.6 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.11.1 // indirect
|
||||
github.com/sacloud/packages-go v0.0.9 // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/softlayer/softlayer-go v1.0.6 // indirect
|
||||
github.com/softlayer/softlayer-go v1.1.2 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/spf13/afero v1.9.3 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/transip/gotransip/v6 v6.17.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/transip/gotransip/v6 v6.20.0 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
||||
github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3 // indirect
|
||||
github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.opentelemetry.io/otel v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/ratelimit v0.2.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/oauth2 v0.4.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/tools v0.5.0 // indirect
|
||||
google.golang.org/api v0.107.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/oauth2 v0.12.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/api v0.143.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
|
||||
google.golang.org/grpc v1.53.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
|
||||
google.golang.org/grpc v1.58.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.6.5 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.102.0
|
||||
|
@@ -13,8 +13,7 @@ ConnectionConfig:
|
||||
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
|
||||
BufferSize: 64 # The internal cache size of each connection, kB
|
||||
Nodes:
|
||||
-
|
||||
PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
||||
- PanelType: "SSpanel" # Panel type: SSpanel, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
|
||||
ApiConfig:
|
||||
ApiHost: "http://127.0.0.1:667"
|
||||
ApiKey: "123"
|
||||
@@ -22,10 +21,11 @@ Nodes:
|
||||
NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan, Shadowsocks-Plugin
|
||||
Timeout: 30 # Timeout for the api request
|
||||
EnableVless: false # Enable Vless for V2ray Type
|
||||
EnableXTLS: false # Enable XTLS for V2ray and Trojan
|
||||
VlessFlow: "xtls-rprx-vision" # Only support vless
|
||||
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
|
||||
DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
|
||||
RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
|
||||
DisableCustomConfig: false # disable custom config for sspanel
|
||||
ControllerConfig:
|
||||
ListenIP: 0.0.0.0 # IP address you want to listen
|
||||
SendIP: 0.0.0.0 # IP address you want to send pacakage
|
||||
@@ -47,12 +47,26 @@ Nodes:
|
||||
Expiry: 60 # Expiry time (second)
|
||||
EnableFallback: false # Only support for Trojan and Vless
|
||||
FallBackConfigs: # Support multiple fallbacks
|
||||
-
|
||||
SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||
- SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||
Alpn: # Alpn, Empty for any
|
||||
Path: # HTTP PATH, Empty for any
|
||||
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
|
||||
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
|
||||
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||
DisableLocalREALITYConfig: false # disable local reality config
|
||||
EnableREALITY: true # Enable REALITY
|
||||
REALITYConfigs:
|
||||
Show: true # Show REALITY debug
|
||||
Dest: www.smzdm.com:443 # Required, Same as fallback
|
||||
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||
ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
|
||||
- www.smzdm.com
|
||||
PrivateKey: YOUR_PRIVATE_KEY # Required, execute './xray x25519' to generate.
|
||||
MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
|
||||
MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
|
||||
MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
|
||||
ShortIds: # Required, list of available shortIds for the client, can be used to differentiate between different clients.
|
||||
- ""
|
||||
- 0123456789abcdef
|
||||
CertConfig:
|
||||
CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
|
||||
CertDomain: "node1.test.com" # Domain to cert
|
||||
@@ -63,29 +77,66 @@ Nodes:
|
||||
DNSEnv: # DNS ENV option used by DNS provider
|
||||
ALICLOUD_ACCESS_KEY: aaa
|
||||
ALICLOUD_SECRET_KEY: bbb
|
||||
# -
|
||||
# PanelType: "NewV2board" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
||||
# ApiConfig:
|
||||
# ApiHost: "http://127.0.0.1:668"
|
||||
# ApiKey: "123"
|
||||
# NodeID: 4
|
||||
# NodeType: Shadowsocks # Node type: V2ray, Shadowsocks, Trojan
|
||||
# Timeout: 30 # Timeout for the api request
|
||||
# EnableVless: false # Enable Vless for V2ray Type
|
||||
# EnableXTLS: false # Enable XTLS for V2ray and Trojan
|
||||
# SpeedLimit: 0 # Mbps, Local settings will replace remote settings
|
||||
# DeviceLimit: 0 # Local settings will replace remote settings
|
||||
# ControllerConfig:
|
||||
# ListenIP: 0.0.0.0 # IP address you want to listen
|
||||
# UpdatePeriodic: 10 # Time to update the nodeinfo, how many sec.
|
||||
# EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
||||
# CertConfig:
|
||||
# CertMode: dns # Option about how to get certificate: none, file, http, dns
|
||||
# CertDomain: "node1.test.com" # Domain to cert
|
||||
# CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
|
||||
# KeyFile: /etc/XrayR/cert/node1.test.com.pem
|
||||
# Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
||||
# Email: test@me.com
|
||||
# DNSEnv: # DNS ENV option used by DNS provider
|
||||
# ALICLOUD_ACCESS_KEY: aaa
|
||||
# ALICLOUD_SECRET_KEY: bbb
|
||||
|
||||
# - PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
|
||||
# ApiConfig:
|
||||
# ApiHost: "http://127.0.0.1:668"
|
||||
# ApiKey: "123"
|
||||
# NodeID: 41
|
||||
# NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan, Shadowsocks-Plugin
|
||||
# Timeout: 30 # Timeout for the api request
|
||||
# EnableVless: false # Enable Vless for V2ray Type
|
||||
# VlessFlow: "xtls-rprx-vision" # Only support vless
|
||||
# SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
|
||||
# DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
|
||||
# RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
|
||||
# ControllerConfig:
|
||||
# ListenIP: 0.0.0.0 # IP address you want to listen
|
||||
# SendIP: 0.0.0.0 # IP address you want to send pacakage
|
||||
# UpdatePeriodic: 60 # Time to update the nodeinfo, how many sec.
|
||||
# EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
||||
# DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
||||
# EnableProxyProtocol: false # Only works for WebSocket and TCP
|
||||
# AutoSpeedLimitConfig:
|
||||
# Limit: 0 # Warned speed. Set to 0 to disable AutoSpeedLimit (mbps)
|
||||
# WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately.
|
||||
# LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps)
|
||||
# LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
|
||||
# GlobalDeviceLimitConfig:
|
||||
# Enable: false # Enable the global device limit of a user
|
||||
# RedisAddr: 127.0.0.1:6379 # The redis server address
|
||||
# RedisPassword: YOUR PASSWORD # Redis password
|
||||
# RedisDB: 0 # Redis DB
|
||||
# Timeout: 5 # Timeout for redis request
|
||||
# Expiry: 60 # Expiry time (second)
|
||||
# EnableFallback: false # Only support for Trojan and Vless
|
||||
# FallBackConfigs: # Support multiple fallbacks
|
||||
# - SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||
# Alpn: # Alpn, Empty for any
|
||||
# Path: # HTTP PATH, Empty for any
|
||||
# Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
|
||||
# ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||
# EnableREALITY: true # Enable REALITY
|
||||
# REALITYConfigs:
|
||||
# Show: true # Show REALITY debug
|
||||
# Dest: www.smzdm.com:443 # Required, Same as fallback
|
||||
# ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||
# ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
|
||||
# - www.smzdm.com
|
||||
# PrivateKey: YOUR_PRIVATE_KEY # Required, execute './xray x25519' to generate.
|
||||
# MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
|
||||
# MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
|
||||
# MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
|
||||
# ShortIds: # Required, list of available shortIds for the client, can be used to differentiate between different clients.
|
||||
# - ""
|
||||
# - 0123456789abcdef
|
||||
# CertConfig:
|
||||
# CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
|
||||
# CertDomain: "node1.test.com" # Domain to cert
|
||||
# CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
|
||||
# KeyFile: /etc/XrayR/cert/node1.test.com.key
|
||||
# Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
||||
# Email: test@me.com
|
||||
# DNSEnv: # DNS ENV option used by DNS provider
|
||||
# ALICLOUD_ACCESS_KEY: aaa
|
||||
# ALICLOUD_SECRET_KEY: bbb
|
||||
|
@@ -31,7 +31,6 @@ import (
|
||||
_ "github.com/xtls/xray-core/proxy/dokodemo"
|
||||
_ "github.com/xtls/xray-core/proxy/freedom"
|
||||
_ "github.com/xtls/xray-core/proxy/http"
|
||||
_ "github.com/xtls/xray-core/proxy/mtproto"
|
||||
_ "github.com/xtls/xray-core/proxy/shadowsocks"
|
||||
_ "github.com/xtls/xray-core/proxy/socks"
|
||||
_ "github.com/xtls/xray-core/proxy/trojan"
|
||||
@@ -45,11 +44,11 @@ import (
|
||||
_ "github.com/xtls/xray-core/transport/internet/http"
|
||||
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
||||
_ "github.com/xtls/xray-core/transport/internet/quic"
|
||||
_ "github.com/xtls/xray-core/transport/internet/reality"
|
||||
_ "github.com/xtls/xray-core/transport/internet/tcp"
|
||||
_ "github.com/xtls/xray-core/transport/internet/tls"
|
||||
_ "github.com/xtls/xray-core/transport/internet/udp"
|
||||
_ "github.com/xtls/xray-core/transport/internet/websocket"
|
||||
_ "github.com/xtls/xray-core/transport/internet/xtls"
|
||||
|
||||
// Transport headers
|
||||
_ "github.com/xtls/xray-core/transport/internet/headers/http"
|
||||
|
@@ -24,7 +24,7 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
codename = "XrayR"
|
||||
intro = "A Xray backend that supports many panels"
|
||||
)
|
||||
|
@@ -6,10 +6,11 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||
"github.com/XrayR-project/XrayR/api/newV2board"
|
||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"dario.cat/mergo"
|
||||
"github.com/r3labs/diff/v2"
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
"github.com/XrayR-project/XrayR/api/pmpanel"
|
||||
"github.com/XrayR-project/XrayR/api/proxypanel"
|
||||
"github.com/XrayR-project/XrayR/api/sspanel"
|
||||
"github.com/XrayR-project/XrayR/api/v2board"
|
||||
"github.com/XrayR-project/XrayR/api/v2raysocks"
|
||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
||||
"github.com/XrayR-project/XrayR/service"
|
||||
@@ -177,9 +177,6 @@ func (p *Panel) Start() {
|
||||
switch nodeConfig.PanelType {
|
||||
case "SSpanel":
|
||||
apiClient = sspanel.New(nodeConfig.ApiConfig)
|
||||
// todo Deprecated after 2023.6.1
|
||||
case "V2board":
|
||||
apiClient = v2board.New(nodeConfig.ApiConfig)
|
||||
case "NewV2board":
|
||||
apiClient = newV2board.New(nodeConfig.ApiConfig)
|
||||
case "PMpanel":
|
||||
@@ -188,6 +185,8 @@ func (p *Panel) Start() {
|
||||
apiClient = proxypanel.New(nodeConfig.ApiConfig)
|
||||
case "V2RaySocks":
|
||||
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
|
||||
case "GoV2Panel":
|
||||
apiClient = gov2panel.New(nodeConfig.ApiConfig)
|
||||
default:
|
||||
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
|
||||
}
|
||||
|
@@ -6,21 +6,24 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ListenIP string `mapstructure:"ListenIP"`
|
||||
SendIP string `mapstructure:"SendIP"`
|
||||
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
|
||||
CertConfig *mylego.CertConfig `mapstructure:"CertConfig"`
|
||||
EnableDNS bool `mapstructure:"EnableDNS"`
|
||||
DNSType string `mapstructure:"DNSType"`
|
||||
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
|
||||
DisableGetRule bool `mapstructure:"DisableGetRule"`
|
||||
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
|
||||
EnableFallback bool `mapstructure:"EnableFallback"`
|
||||
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
|
||||
DisableSniffing bool `mapstructure:"DisableSniffing"`
|
||||
AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"`
|
||||
GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"`
|
||||
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
|
||||
ListenIP string `mapstructure:"ListenIP"`
|
||||
SendIP string `mapstructure:"SendIP"`
|
||||
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
|
||||
CertConfig *mylego.CertConfig `mapstructure:"CertConfig"`
|
||||
EnableDNS bool `mapstructure:"EnableDNS"`
|
||||
DNSType string `mapstructure:"DNSType"`
|
||||
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
|
||||
DisableGetRule bool `mapstructure:"DisableGetRule"`
|
||||
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
|
||||
EnableFallback bool `mapstructure:"EnableFallback"`
|
||||
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
|
||||
DisableSniffing bool `mapstructure:"DisableSniffing"`
|
||||
AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"`
|
||||
GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"`
|
||||
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
|
||||
DisableLocalREALITYConfig bool `mapstructure:"DisableLocalREALITYConfig"`
|
||||
EnableREALITY bool `mapstructure:"EnableREALITY"`
|
||||
REALITYConfigs *REALITYConfig `mapstructure:"REALITYConfigs"`
|
||||
}
|
||||
|
||||
type AutoSpeedLimitConfig struct {
|
||||
@@ -37,3 +40,15 @@ type FallBackConfig struct {
|
||||
Dest string `mapstructure:"Dest"`
|
||||
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
|
||||
}
|
||||
|
||||
type REALITYConfig struct {
|
||||
Show bool `mapstructure:"Show"`
|
||||
Dest string `mapstructure:"Dest"`
|
||||
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
|
||||
ServerNames []string `mapstructure:"ServerNames"`
|
||||
PrivateKey string `mapstructure:"PrivateKey"`
|
||||
MinClientVer string `mapstructure:"MinClientVer"`
|
||||
MaxClientVer string `mapstructure:"MaxClientVer"`
|
||||
MaxTimeDiff uint64 `mapstructure:"MaxTimeDiff"`
|
||||
ShortIds []string `mapstructure:"ShortIds"`
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
@@ -75,6 +76,9 @@ func (c *Controller) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if newNodeInfo.Port == 0 {
|
||||
return errors.New("server port must > 0")
|
||||
}
|
||||
c.nodeInfo = newNodeInfo
|
||||
c.Tag = c.buildNodeTag()
|
||||
|
||||
@@ -140,7 +144,7 @@ func (c *Controller) Start() error {
|
||||
)
|
||||
|
||||
// Check cert service in need
|
||||
if c.nodeInfo.EnableTLS {
|
||||
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
|
||||
c.tasks = append(c.tasks, periodicTask{
|
||||
tag: "cert monitor",
|
||||
Periodic: &task.Periodic{
|
||||
@@ -178,17 +182,26 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
}
|
||||
|
||||
// First fetch Node Info
|
||||
var nodeInfoChanged = true
|
||||
newNodeInfo, err := c.apiClient.GetNodeInfo()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
if err.Error() == api.NodeNotModified {
|
||||
nodeInfoChanged = false
|
||||
newNodeInfo = c.nodeInfo
|
||||
} else {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if newNodeInfo.Port == 0 {
|
||||
return errors.New("server port must > 0")
|
||||
}
|
||||
|
||||
// Update User
|
||||
var usersChanged = true
|
||||
newUserInfo, err := c.apiClient.GetUserList()
|
||||
if err != nil {
|
||||
if err.Error() == "users no change" {
|
||||
if err.Error() == api.UserNotModified {
|
||||
usersChanged = false
|
||||
newUserInfo = c.userList
|
||||
} else {
|
||||
@@ -197,43 +210,48 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
var nodeInfoChanged = false
|
||||
// If nodeInfo changed
|
||||
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
|
||||
// Remove old tag
|
||||
oldTag := c.Tag
|
||||
err := c.removeOldTag(oldTag)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
|
||||
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
|
||||
}
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
// Add new tag
|
||||
c.nodeInfo = newNodeInfo
|
||||
c.Tag = c.buildNodeTag()
|
||||
err = c.addNewTag(newNodeInfo)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
nodeInfoChanged = true
|
||||
// Remove Old limiter
|
||||
if err = c.DeleteInboundLimiter(oldTag); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
if nodeInfoChanged {
|
||||
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
|
||||
// Remove old tag
|
||||
oldTag := c.Tag
|
||||
err := c.removeOldTag(oldTag)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
|
||||
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
|
||||
}
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
// Add new tag
|
||||
c.nodeInfo = newNodeInfo
|
||||
c.Tag = c.buildNodeTag()
|
||||
err = c.addNewTag(newNodeInfo)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
nodeInfoChanged = true
|
||||
// Remove Old limiter
|
||||
if err = c.DeleteInboundLimiter(oldTag); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
nodeInfoChanged = false
|
||||
}
|
||||
}
|
||||
|
||||
// Check Rule
|
||||
if !c.config.DisableGetRule {
|
||||
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
|
||||
log.Printf("Get rule list filed: %s", err)
|
||||
if err.Error() != api.RuleNotModified {
|
||||
log.Printf("Get rule list filed: %s", err)
|
||||
}
|
||||
} else if len(*ruleList) > 0 {
|
||||
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
|
||||
log.Print(err)
|
||||
@@ -384,14 +402,7 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
|
||||
if nodeInfo.EnableVless {
|
||||
users = c.buildVlessUser(userInfo)
|
||||
} else {
|
||||
var alterID uint16 = 0
|
||||
if (c.panelType == "V2board" || c.panelType == "V2RaySocks") && len(*userInfo) > 0 {
|
||||
// use latest userInfo
|
||||
alterID = (*userInfo)[0].AlterID
|
||||
} else {
|
||||
alterID = nodeInfo.AlterID
|
||||
}
|
||||
users = c.buildVmessUser(userInfo, alterID)
|
||||
users = c.buildVmessUser(userInfo)
|
||||
}
|
||||
case "Trojan":
|
||||
users = c.buildTrojanUser(userInfo)
|
||||
@@ -599,7 +610,7 @@ func (c *Controller) logPrefix() string {
|
||||
|
||||
// Check Cert
|
||||
func (c *Controller) certMonitor() error {
|
||||
if c.nodeInfo.EnableTLS {
|
||||
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
|
||||
switch c.config.CertConfig.CertMode {
|
||||
case "dns", "http", "tls":
|
||||
lego, err := mylego.New(c.config.CertConfig)
|
||||
|
@@ -184,27 +184,54 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
|
||||
streamSetting.Network = &transportProtocol
|
||||
|
||||
// Build TLS and XTLS settings
|
||||
if nodeInfo.EnableTLS && config.CertConfig.CertMode != "none" {
|
||||
streamSetting.Security = nodeInfo.TLSType
|
||||
// Build TLS and REALITY settings
|
||||
var isREALITY bool
|
||||
if config.DisableLocalREALITYConfig {
|
||||
if nodeInfo.REALITYConfig != nil && nodeInfo.EnableREALITY {
|
||||
isREALITY = true
|
||||
streamSetting.Security = "reality"
|
||||
|
||||
r := nodeInfo.REALITYConfig
|
||||
streamSetting.REALITYSettings = &conf.REALITYConfig{
|
||||
Show: config.REALITYConfigs.Show,
|
||||
Dest: []byte(`"` + r.Dest + `"`),
|
||||
Xver: r.ProxyProtocolVer,
|
||||
ServerNames: r.ServerNames,
|
||||
PrivateKey: r.PrivateKey,
|
||||
MinClientVer: r.MinClientVer,
|
||||
MaxClientVer: r.MaxClientVer,
|
||||
MaxTimeDiff: r.MaxTimeDiff,
|
||||
ShortIds: r.ShortIds,
|
||||
}
|
||||
}
|
||||
} else if config.EnableREALITY && config.REALITYConfigs != nil {
|
||||
isREALITY = true
|
||||
streamSetting.Security = "reality"
|
||||
|
||||
streamSetting.REALITYSettings = &conf.REALITYConfig{
|
||||
Show: config.REALITYConfigs.Show,
|
||||
Dest: []byte(`"` + config.REALITYConfigs.Dest + `"`),
|
||||
Xver: config.REALITYConfigs.ProxyProtocolVer,
|
||||
ServerNames: config.REALITYConfigs.ServerNames,
|
||||
PrivateKey: config.REALITYConfigs.PrivateKey,
|
||||
MinClientVer: config.REALITYConfigs.MinClientVer,
|
||||
MaxClientVer: config.REALITYConfigs.MaxClientVer,
|
||||
MaxTimeDiff: config.REALITYConfigs.MaxTimeDiff,
|
||||
ShortIds: config.REALITYConfigs.ShortIds,
|
||||
}
|
||||
}
|
||||
|
||||
if !isREALITY && nodeInfo.EnableTLS && config.CertConfig.CertMode != "none" {
|
||||
streamSetting.Security = "tls"
|
||||
certFile, keyFile, err := getCertFile(config.CertConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nodeInfo.TLSType == "tls" {
|
||||
tlsSettings := &conf.TLSConfig{
|
||||
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
|
||||
}
|
||||
tlsSettings.Certs = append(tlsSettings.Certs, &conf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
|
||||
|
||||
streamSetting.TLSSettings = tlsSettings
|
||||
} else if nodeInfo.TLSType == "xtls" {
|
||||
xtlsSettings := &conf.XTLSConfig{
|
||||
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
|
||||
}
|
||||
xtlsSettings.Certs = append(xtlsSettings.Certs, &conf.XTLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
|
||||
streamSetting.XTLSSettings = xtlsSettings
|
||||
tlsSettings := &conf.TLSConfig{
|
||||
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
|
||||
}
|
||||
tlsSettings.Certs = append(tlsSettings.Certs, &conf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
|
||||
streamSetting.TLSSettings = tlsSettings
|
||||
}
|
||||
|
||||
// Support ProxyProtocol for any transport protocol
|
||||
|
@@ -19,7 +19,6 @@ func TestBuildV2ray(t *testing.T) {
|
||||
Host: "test.test.tk",
|
||||
Path: "v2ray",
|
||||
EnableTLS: false,
|
||||
TLSType: "tls",
|
||||
}
|
||||
certConfig := &mylego.CertConfig{
|
||||
CertMode: "http",
|
||||
@@ -47,7 +46,6 @@ func TestBuildTrojan(t *testing.T) {
|
||||
Host: "trojan.test.tk",
|
||||
Path: "v2ray",
|
||||
EnableTLS: false,
|
||||
TLSType: "tls",
|
||||
}
|
||||
DNSEnv := make(map[string]string)
|
||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||
@@ -79,7 +77,6 @@ func TestBuildSS(t *testing.T) {
|
||||
Host: "test.test.tk",
|
||||
Path: "v2ray",
|
||||
EnableTLS: false,
|
||||
TLSType: "tls",
|
||||
}
|
||||
DNSEnv := make(map[string]string)
|
||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||
|
@@ -25,12 +25,11 @@ var AEADMethod = map[shadowsocks.CipherType]uint8{
|
||||
shadowsocks.CipherType_XCHACHA20_POLY1305: 0,
|
||||
}
|
||||
|
||||
func (c *Controller) buildVmessUser(userInfo *[]api.UserInfo, serverAlterID uint16) (users []*protocol.User) {
|
||||
func (c *Controller) buildVmessUser(userInfo *[]api.UserInfo) (users []*protocol.User) {
|
||||
users = make([]*protocol.User, len(*userInfo))
|
||||
for i, user := range *userInfo {
|
||||
vmessAccount := &conf.VMessAccount{
|
||||
ID: user.UUID,
|
||||
AlterIds: serverAlterID,
|
||||
Security: "auto",
|
||||
}
|
||||
users[i] = &protocol.User{
|
||||
@@ -47,7 +46,7 @@ func (c *Controller) buildVlessUser(userInfo *[]api.UserInfo) (users []*protocol
|
||||
for i, user := range *userInfo {
|
||||
vlessAccount := &vless.Account{
|
||||
Id: user.UUID,
|
||||
Flow: "xtls-rprx-vision,none",
|
||||
Flow: c.nodeInfo.VlessFlow,
|
||||
}
|
||||
users[i] = &protocol.User{
|
||||
Level: 0,
|
||||
@@ -63,7 +62,6 @@ func (c *Controller) buildTrojanUser(userInfo *[]api.UserInfo) (users []*protoco
|
||||
for i, user := range *userInfo {
|
||||
trojanAccount := &trojan.Account{
|
||||
Password: user.UUID,
|
||||
Flow: "xtls-rprx-direct",
|
||||
}
|
||||
users[i] = &protocol.User{
|
||||
Level: 0,
|
||||
|
Reference in New Issue
Block a user