x
This commit is contained in:
+253
@@ -0,0 +1,253 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/cdle/sillyplus/core/storage"
|
||||
"github.com/cdle/sillyplus/utils"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// type ProxyKey struct {
|
||||
// }
|
||||
|
||||
type ProxyConfig struct {
|
||||
Type string `json:"type"`
|
||||
Server string `json:"server"`
|
||||
Port int `json:"port"`
|
||||
Name string `json:"name"`
|
||||
// ProxyKey
|
||||
// RuleMatcher *RuleMatcher `json:"-"`
|
||||
Conn C.Proxy `json:"-"`
|
||||
Rules []string `json:"rules"`
|
||||
Plugins []string `json:"plugins"`
|
||||
|
||||
Cipher string `json:"cipher,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
SkipCertVerify bool `json:"skip-cert-verify,omitempty"`
|
||||
TLS bool `json:"tls,omitempty"`
|
||||
CreatedAt int `json:"created_at,omitempty"`
|
||||
Remark string `json:"remark,omitempty"`
|
||||
Enable bool `json:"enable,omitempty"`
|
||||
// UDP bool `json:"udp,omitempty"`
|
||||
// Plugin string `json:"plugin,omitempty"`
|
||||
// PluginOpts map[string]string `json:"plugin-opts,omitempty"`
|
||||
// Obfs string `json:"obfs,omitempty"`
|
||||
// ObfsOpts map[string]string `json:"obfs-opts,omitempty"`
|
||||
// Enable bool `json:"enable,omitempty"`
|
||||
// ExcludeIPs []string `json:"exclude-ip,omitempty"`
|
||||
// ProxyGroups []string `json:"proxy-groups,omitempty"`
|
||||
|
||||
}
|
||||
|
||||
var Proxies sync.Map
|
||||
var proxies = MakeBucket("proxies")
|
||||
|
||||
func GetProxyTransport(rawURL string, uuid string, params map[string]interface{}) (C.Conn, error) {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
switch u.Scheme {
|
||||
case "https":
|
||||
port = "443"
|
||||
case "http":
|
||||
port = "80"
|
||||
default:
|
||||
err = fmt.Errorf("%s scheme not Support", rawURL)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
addr := C.Metadata{
|
||||
Host: u.Hostname(),
|
||||
DstIP: nil,
|
||||
DstPort: port,
|
||||
}
|
||||
var p *ProxyConfig
|
||||
if params != nil {
|
||||
params["port"] = utils.Int(params["port"])
|
||||
conn, err := adapter.ParseProxy(params)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("代理配置错误:%v", err)
|
||||
return nil, err
|
||||
}
|
||||
i, err := conn.DialContext(context.Background(), &addr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("代理连接错误:%v", err)
|
||||
}
|
||||
return i, err
|
||||
}
|
||||
var plugins = []*ProxyConfig{}
|
||||
Proxies.Range(func(key, value any) bool {
|
||||
cfg := value.(*ProxyConfig)
|
||||
if Contains(cfg.Rules, addr.Host) {
|
||||
p = cfg
|
||||
return false
|
||||
}
|
||||
if Contains(cfg.Plugins, uuid) {
|
||||
plugins = append(plugins, cfg)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if p != nil {
|
||||
i, err := p.Conn.DialContext(context.Background(), &addr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s(%s)代理错误:%v", p.Type, p.Name, err)
|
||||
}
|
||||
return i, err
|
||||
}
|
||||
if len(plugins) != 0 {
|
||||
i, err := plugins[0].Conn.DialContext(context.Background(), &addr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s(%s)代理错误:%v", p.Type, p.Name, err)
|
||||
}
|
||||
return i, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxies.Foreach(func(b1, b2 []byte) error {
|
||||
new := string(b2)
|
||||
var ncfg = ProxyConfig{}
|
||||
if strings.HasPrefix(new, "o:") {
|
||||
err := json.Unmarshal([]byte(strings.Replace(new, "o:", "", 1)), &ncfg)
|
||||
if err != nil {
|
||||
console.Log("无法解析的代理数据:", err)
|
||||
return nil
|
||||
}
|
||||
// ncfg.RuleMatcher, err = preprocessRules(ncfg.Rules)
|
||||
if err != nil {
|
||||
console.Log("无法处理的代理匹配规则:", err)
|
||||
return nil
|
||||
}
|
||||
p, err := adapter.ParseProxy(structToMap(ncfg))
|
||||
if err != nil {
|
||||
console.Log("无法解析的代理:", err)
|
||||
return nil
|
||||
}
|
||||
ncfg.Conn = p
|
||||
// fmt.Println("===", string(utils.JsonMarshal(ncfg)))
|
||||
// fmt.Println(ncfg.RuleMatcher.Match("106.52.87.206"))
|
||||
// fmt.Println(ncfg.RuleMatcher.Match("api.telegram.org"))
|
||||
Proxies.Store(string(b1), &ncfg)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
storage.Watch(proxies, "*", func(old, new, key string) *storage.Final {
|
||||
_, err := uuid.Parse(key)
|
||||
if err != nil {
|
||||
return &storage.Final{
|
||||
Error: errors.New("非法的UUID"),
|
||||
}
|
||||
}
|
||||
// var ocfg = ProxyConfig{}
|
||||
// if strings.HasPrefix(new, "o:") {
|
||||
// json.Unmarshal([]byte(strings.Replace(old, "o:", "", 1)), &ocfg)
|
||||
// }
|
||||
// var okey = ProxyKey{
|
||||
// Type: ocfg.Type,
|
||||
// Server: ocfg.Server,
|
||||
// Port: ocfg.Port,
|
||||
// }
|
||||
// var nkey = ProxyKey{}
|
||||
var ncfg = ProxyConfig{}
|
||||
if new == "" { //删除逻辑
|
||||
Proxies.Delete(key)
|
||||
return nil
|
||||
}
|
||||
if strings.HasPrefix(new, "o:") {
|
||||
err := json.Unmarshal([]byte(strings.Replace(new, "o:", "", 1)), &ncfg)
|
||||
if err != nil {
|
||||
return &storage.Final{
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
// nkey = ProxyKey{
|
||||
// Type: ncfg.Type,
|
||||
// Server: ncfg.Server,
|
||||
// Port: ncfg.Port,
|
||||
// }
|
||||
}
|
||||
// ov, ok := Proxies.Load(nkey)
|
||||
// if ok && (!IsDifferent(ocfg, ncfg, []string{"Name", "UUID", "Rules", "Plugins"}) || checkProxy(ov.(C.Proxy))) { //代理依旧有效
|
||||
// return nil
|
||||
// }
|
||||
// ncfg.RuleMatcher, err = preprocessRules(ncfg.Rules)
|
||||
if err != nil {
|
||||
return &storage.Final{
|
||||
Error: errors.New("不支持的代理匹配规则:" + err.Error()),
|
||||
}
|
||||
}
|
||||
p, err := adapter.ParseProxy(structToMap(ncfg))
|
||||
if err != nil {
|
||||
return &storage.Final{
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
ncfg.Conn = p
|
||||
ncfg.CreatedAt = int(time.Now().Unix())
|
||||
Proxies.Store(key, &ncfg)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func structToMap(s interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
d, _ := json.Marshal(s)
|
||||
json.Unmarshal(d, &result)
|
||||
return result
|
||||
}
|
||||
|
||||
func IsDifferent(a, b interface{}, ignoreFields []string) bool {
|
||||
aVal := reflect.ValueOf(a)
|
||||
bVal := reflect.ValueOf(b)
|
||||
|
||||
if aVal.Kind() != reflect.Struct || bVal.Kind() != reflect.Struct {
|
||||
// 如果不是 struct 类型,则返回 true(不同)
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取 struct 的字段数
|
||||
numFields := aVal.NumField()
|
||||
|
||||
// 遍历 struct 的所有字段,检查是否有不同
|
||||
for i := 0; i < numFields; i++ {
|
||||
aField := aVal.Field(i)
|
||||
bField := bVal.Field(i)
|
||||
|
||||
// 获取字段名
|
||||
fieldName := aVal.Type().Field(i).Name
|
||||
|
||||
// 如果字段名在 ignoreFields 中,则跳过比较
|
||||
if Contains(ignoreFields, fieldName) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果字段类型不同,则返回 true(不同)
|
||||
if aField.Type() != bField.Type() {
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果字段值不同,则返回 true(不同)
|
||||
if !reflect.DeepEqual(aField.Interface(), bField.Interface()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有字段都相同,则返回 false(相同)
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user