V2bX/api/panel/node.go

290 lines
6.9 KiB
Go
Raw Permalink Normal View History

package panel
2022-06-04 00:05:46 -04:00
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"reflect"
"strconv"
2023-01-06 12:59:24 -05:00
"strings"
"time"
2023-07-14 00:54:09 -04:00
"github.com/goccy/go-json"
2022-06-04 00:05:46 -04:00
)
2023-08-19 08:06:42 -04:00
// Security type
const (
None = 0
Tls = 1
Reality = 2
)
type NodeInfo struct {
Id int
Type string
Security int
PushInterval time.Duration
PullInterval time.Duration
RawDNS RawDNS
Rules Rules
// origin
VAllss *VAllssNode
Shadowsocks *ShadowsocksNode
Trojan *TrojanNode
Hysteria *HysteriaNode
Hysteria2 *Hysteria2Node
2023-08-19 08:06:42 -04:00
Common *CommonNode
}
type CommonNode struct {
Host string `json:"host"`
ServerPort int `json:"server_port"`
ServerName string `json:"server_name"`
Routes []Route `json:"routes"`
BaseConfig *BaseConfig `json:"base_config"`
}
type Route struct {
2023-07-27 13:07:56 -04:00
Id int `json:"id"`
Match interface{} `json:"match"`
Action string `json:"action"`
ActionValue string `json:"action_value"`
}
type BaseConfig struct {
PushInterval any `json:"push_interval"`
PullInterval any `json:"pull_interval"`
2022-09-14 21:24:14 -04:00
}
2022-06-04 00:05:46 -04:00
2023-08-19 08:06:42 -04:00
// VAllssNode is vmess and vless node info
type VAllssNode struct {
CommonNode
2023-08-30 03:34:37 -04:00
Tls int `json:"tls"`
TlsSettings TlsSettings `json:"tls_settings"`
TlsSettingsBack *TlsSettings `json:"tlsSettings"`
Network string `json:"network"`
NetworkSettings json.RawMessage `json:"network_settings"`
NetworkSettingsBack json.RawMessage `json:"networkSettings"`
ServerName string `json:"server_name"`
2023-08-19 08:06:42 -04:00
// vless only
Flow string `json:"flow"`
RealityConfig RealityConfig `json:"-"`
}
2023-08-19 08:06:42 -04:00
type TlsSettings struct {
2023-08-23 10:06:36 -04:00
ServerName string `json:"server_name"`
2023-11-17 21:59:58 -05:00
Dest string `json:"dest"`
2023-08-23 10:06:36 -04:00
ServerPort string `json:"server_port"`
ShortId string `json:"short_id"`
2023-08-23 11:55:57 -04:00
PrivateKey string `json:"private_key"`
Xver uint64 `json:"xver,string"`
2023-08-19 08:06:42 -04:00
}
type RealityConfig struct {
Xver uint64 `json:"Xver"`
MinClientVer string `json:"MinClientVer"`
MaxClientVer string `json:"MaxClientVer"`
2023-08-23 10:06:36 -04:00
MaxTimeDiff string `json:"MaxTimeDiff"`
2023-08-19 08:06:42 -04:00
}
type ShadowsocksNode struct {
CommonNode
Cipher string `json:"cipher"`
ServerKey string `json:"server_key"`
}
2023-12-18 10:17:16 -05:00
type TrojanNode struct {
CommonNode
Network string `json:"network"`
NetworkSettings json.RawMessage `json:"networkSettings"`
}
2023-08-19 08:06:42 -04:00
type HysteriaNode struct {
CommonNode
UpMbps int `json:"up_mbps"`
DownMbps int `json:"down_mbps"`
Obfs string `json:"obfs"`
}
type Hysteria2Node struct {
CommonNode
UpMbps int `json:"up_mbps"`
DownMbps int `json:"down_mbps"`
ObfsType string `json:"obfs"`
ObfsPassword string `json:"obfs-password"`
}
type RawDNS struct {
DNSMap map[string]map[string]interface{}
DNSJson []byte
}
2023-07-20 09:14:18 -04:00
type Rules struct {
Regexp []string
Protocol []string
}
func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
const path = "/api/v1/server/UniProxy/config"
2023-06-24 19:49:51 -04:00
r, err := c.client.
R().
2023-07-21 14:38:07 -04:00
SetHeader("If-None-Match", c.nodeEtag).
ForceContentType("application/json").
2023-06-24 19:49:51 -04:00
Get(path)
if r.StatusCode() == 304 {
return nil, nil
}
hash := sha256.Sum256(r.Body())
newBodyHash := hex.EncodeToString(hash[:])
if c.responseBodyHash == newBodyHash {
return nil, nil
}
c.responseBodyHash = newBodyHash
c.nodeEtag = r.Header().Get("ETag")
if err = c.checkResponse(r, path, err); err != nil {
return nil, err
2022-06-04 00:05:46 -04:00
}
2024-01-25 09:06:37 -05:00
if r != nil {
defer func() {
if r.RawBody() != nil {
r.RawBody().Close()
}
}()
} else {
return nil, fmt.Errorf("received nil response")
2022-06-04 00:05:46 -04:00
}
2023-06-09 00:36:49 -04:00
node = &NodeInfo{
Id: c.NodeId,
Type: c.NodeType,
RawDNS: RawDNS{
DNSMap: make(map[string]map[string]interface{}),
DNSJson: []byte(""),
},
2023-06-09 00:36:49 -04:00
}
2023-08-19 08:06:42 -04:00
// parse protocol params
var cm *CommonNode
switch c.NodeType {
case "vmess", "vless":
rsp := &VAllssNode{}
err = json.Unmarshal(r.Body(), rsp)
if err != nil {
return nil, fmt.Errorf("decode v2ray params error: %s", err)
}
2023-08-30 03:34:37 -04:00
if len(rsp.NetworkSettingsBack) > 0 {
rsp.NetworkSettings = rsp.NetworkSettingsBack
rsp.NetworkSettingsBack = nil
}
if rsp.TlsSettingsBack != nil {
rsp.TlsSettings = *rsp.TlsSettingsBack
rsp.TlsSettingsBack = nil
}
2023-08-19 08:06:42 -04:00
cm = &rsp.CommonNode
node.VAllss = rsp
node.Security = node.VAllss.Tls
case "shadowsocks":
rsp := &ShadowsocksNode{}
err = json.Unmarshal(r.Body(), rsp)
if err != nil {
return nil, fmt.Errorf("decode shadowsocks params error: %s", err)
2023-08-19 08:06:42 -04:00
}
cm = &rsp.CommonNode
node.Shadowsocks = rsp
node.Security = None
case "trojan":
rsp := &TrojanNode{}
err = json.Unmarshal(r.Body(), rsp)
if err != nil {
return nil, fmt.Errorf("decode trojan params error: %s", err)
2023-08-19 08:06:42 -04:00
}
2023-12-18 10:17:16 -05:00
cm = &rsp.CommonNode
2023-08-19 08:06:42 -04:00
node.Trojan = rsp
node.Security = Tls
case "hysteria":
rsp := &HysteriaNode{}
err = json.Unmarshal(r.Body(), rsp)
if err != nil {
return nil, fmt.Errorf("decode hysteria params error: %s", err)
2023-08-19 08:06:42 -04:00
}
cm = &rsp.CommonNode
node.Hysteria = rsp
node.Security = Tls
case "hysteria2":
rsp := &Hysteria2Node{}
err = json.Unmarshal(r.Body(), rsp)
if err != nil {
return nil, fmt.Errorf("decode hysteria2 params error: %s", err)
}
cm = &rsp.CommonNode
node.Hysteria2 = rsp
node.Security = Tls
}
2023-07-27 13:07:56 -04:00
2023-08-19 08:06:42 -04:00
// parse rules and dns
for i := range cm.Routes {
var matchs []string
2023-08-19 08:06:42 -04:00
if _, ok := cm.Routes[i].Match.(string); ok {
matchs = strings.Split(cm.Routes[i].Match.(string), ",")
} else if _, ok = cm.Routes[i].Match.([]string); ok {
matchs = cm.Routes[i].Match.([]string)
} else {
2023-08-19 08:06:42 -04:00
temp := cm.Routes[i].Match.([]interface{})
matchs = make([]string, len(temp))
for i := range temp {
matchs[i] = temp[i].(string)
2023-01-06 12:59:24 -05:00
}
}
2023-08-19 08:06:42 -04:00
switch cm.Routes[i].Action {
case "block":
2023-01-06 12:59:24 -05:00
for _, v := range matchs {
2023-07-20 09:14:18 -04:00
if strings.HasPrefix(v, "protocol:") {
// protocol
node.Rules.Protocol = append(node.Rules.Protocol, strings.TrimPrefix(v, "protocol:"))
} else {
// domain
node.Rules.Regexp = append(node.Rules.Regexp, strings.TrimPrefix(v, "regexp:"))
}
2023-01-06 12:59:24 -05:00
}
case "dns":
var domains []string
domains = append(domains, matchs...)
2023-07-19 00:03:09 -04:00
if matchs[0] != "main" {
node.RawDNS.DNSMap[strconv.Itoa(i)] = map[string]interface{}{
2023-08-19 08:06:42 -04:00
"address": cm.Routes[i].ActionValue,
"domains": domains,
2023-07-19 00:03:09 -04:00
}
2023-07-27 13:07:56 -04:00
} else {
dns := []byte(strings.Join(matchs[1:], ""))
node.RawDNS.DNSJson = dns
2023-07-19 00:03:09 -04:00
}
2022-06-04 00:05:46 -04:00
}
}
2023-08-19 08:06:42 -04:00
// set interval
node.PushInterval = intervalToTime(cm.BaseConfig.PushInterval)
node.PullInterval = intervalToTime(cm.BaseConfig.PullInterval)
node.Common = cm
// clear
cm.Routes = nil
cm.BaseConfig = nil
return node, nil
2022-06-04 00:05:46 -04:00
}
func intervalToTime(i interface{}) time.Duration {
switch reflect.TypeOf(i).Kind() {
case reflect.Int:
return time.Duration(i.(int)) * time.Second
case reflect.String:
i, _ := strconv.Atoi(i.(string))
return time.Duration(i) * time.Second
case reflect.Float64:
return time.Duration(i.(float64)) * time.Second
2023-05-19 00:38:16 -04:00
default:
return time.Duration(reflect.ValueOf(i).Int()) * time.Second
}
}