2022-08-16 01:04:33 -04:00
|
|
|
package panel
|
2022-06-04 00:05:46 -04:00
|
|
|
|
|
|
|
import (
|
2024-02-06 06:26:30 -05:00
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/hex"
|
2023-06-08 10:46:33 -04:00
|
|
|
"fmt"
|
2023-05-17 21:11:28 -04:00
|
|
|
"reflect"
|
2022-12-18 10:31:06 -05:00
|
|
|
"strconv"
|
2023-01-06 12:59:24 -05:00
|
|
|
"strings"
|
2023-05-17 21:11:28 -04:00
|
|
|
"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
|
2023-11-17 17:05:28 -05:00
|
|
|
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"`
|
2022-12-18 10:31:06 -05:00
|
|
|
}
|
2023-06-08 10:46:33 -04:00
|
|
|
|
2022-12-18 10:31:06 -05:00
|
|
|
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"`
|
2022-12-18 10:31:06 -05:00
|
|
|
}
|
|
|
|
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
|
2023-08-30 02:24:09 -04:00
|
|
|
Flow string `json:"flow"`
|
|
|
|
RealityConfig RealityConfig `json:"-"`
|
2023-06-08 10:46:33 -04:00
|
|
|
}
|
|
|
|
|
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"`
|
2024-03-13 09:15:04 -04:00
|
|
|
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
|
2023-06-08 10:46:33 -04:00
|
|
|
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
|
2023-06-08 10:46:33 -04:00
|
|
|
UpMbps int `json:"up_mbps"`
|
|
|
|
DownMbps int `json:"down_mbps"`
|
|
|
|
Obfs string `json:"obfs"`
|
|
|
|
}
|
|
|
|
|
2023-11-17 17:05:28 -05:00
|
|
|
type Hysteria2Node struct {
|
|
|
|
CommonNode
|
|
|
|
UpMbps int `json:"up_mbps"`
|
|
|
|
DownMbps int `json:"down_mbps"`
|
|
|
|
ObfsType string `json:"obfs"`
|
|
|
|
ObfsPassword string `json:"obfs-password"`
|
|
|
|
}
|
|
|
|
|
2023-08-14 13:23:31 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-06-08 10:46:33 -04:00
|
|
|
func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
|
2022-12-18 10:31:06 -05:00
|
|
|
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).
|
2024-02-06 06:26:30 -05:00
|
|
|
ForceContentType("application/json").
|
2023-06-24 19:49:51 -04:00
|
|
|
Get(path)
|
2024-02-06 06:26:30 -05:00
|
|
|
|
|
|
|
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")
|
2022-12-18 10:31:06 -05:00
|
|
|
if err = c.checkResponse(r, path, err); err != nil {
|
2023-11-20 14:13:14 -05:00
|
|
|
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,
|
2023-08-14 13:23:31 -04:00
|
|
|
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 {
|
2023-11-17 17:05:28 -05:00
|
|
|
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 {
|
2023-11-17 17:05:28 -05:00
|
|
|
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 {
|
2023-11-17 17:05:28 -05:00
|
|
|
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
|
2023-11-17 17:05:28 -05:00
|
|
|
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-06-08 10:46:33 -04:00
|
|
|
}
|
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 {
|
2023-06-24 20:22:05 -04:00
|
|
|
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)
|
2023-06-24 20:22:05 -04:00
|
|
|
} else {
|
2023-08-19 08:06:42 -04:00
|
|
|
temp := cm.Routes[i].Match.([]interface{})
|
2023-06-24 20:22:05 -04:00
|
|
|
matchs = make([]string, len(temp))
|
|
|
|
for i := range temp {
|
|
|
|
matchs[i] = temp[i].(string)
|
2023-01-06 12:59:24 -05:00
|
|
|
}
|
2023-06-24 20:22:05 -04:00
|
|
|
}
|
2023-08-19 08:06:42 -04:00
|
|
|
switch cm.Routes[i].Action {
|
2023-06-24 20:22:05 -04:00
|
|
|
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
|
|
|
}
|
2023-06-24 20:22:05 -04:00
|
|
|
case "dns":
|
2023-08-14 13:23:31 -04:00
|
|
|
var domains []string
|
2023-11-20 14:13:14 -05:00
|
|
|
domains = append(domains, matchs...)
|
2023-07-19 00:03:09 -04:00
|
|
|
if matchs[0] != "main" {
|
2023-08-14 13:23:31 -04:00
|
|
|
node.RawDNS.DNSMap[strconv.Itoa(i)] = map[string]interface{}{
|
2023-08-19 08:06:42 -04:00
|
|
|
"address": cm.Routes[i].ActionValue,
|
2023-08-14 13:23:31 -04:00
|
|
|
"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:], ""))
|
2023-08-14 13:23:31 -04:00
|
|
|
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
|
|
|
|
|
2024-02-06 06:26:30 -05:00
|
|
|
return node, nil
|
2022-06-04 00:05:46 -04:00
|
|
|
}
|
2023-05-17 21:11:28 -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
|
2023-05-17 21:11:28 -04:00
|
|
|
}
|
|
|
|
}
|