mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
140 lines
4.0 KiB
Go
140 lines
4.0 KiB
Go
package proxy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
const chainSocks5Prefix = "chain+socks5://"
|
|
|
|
type chainSocks5Hop struct {
|
|
Protocol string `json:"protocol"`
|
|
Server string `json:"server"`
|
|
Port int `json:"port"`
|
|
Username string `json:"username,omitempty"`
|
|
Password string `json:"password,omitempty"`
|
|
}
|
|
|
|
type chainSocks5Config struct {
|
|
LocalPort int `json:"localPort,omitempty"`
|
|
First chainSocks5Hop `json:"first"`
|
|
Second chainSocks5Hop `json:"second"`
|
|
}
|
|
|
|
func IsChainSocks5Proxy(src string) bool {
|
|
return strings.HasPrefix(strings.ToLower(strings.TrimSpace(src)), chainSocks5Prefix)
|
|
}
|
|
|
|
func ParseChainSocks5Config(src string) (*chainSocks5Config, error) {
|
|
raw := strings.TrimSpace(src)
|
|
if !IsChainSocks5Proxy(raw) {
|
|
return nil, fmt.Errorf("不是链式代理配置")
|
|
}
|
|
encoded := raw[len(chainSocks5Prefix):]
|
|
if strings.TrimSpace(encoded) == "" {
|
|
return nil, fmt.Errorf("链式代理配置为空")
|
|
}
|
|
|
|
decoded, err := url.QueryUnescape(encoded)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("链式代理配置解码失败: %w", err)
|
|
}
|
|
|
|
var cfg chainSocks5Config
|
|
if err := json.Unmarshal([]byte(decoded), &cfg); err != nil {
|
|
return nil, fmt.Errorf("链式代理配置 JSON 解析失败: %w", err)
|
|
}
|
|
|
|
if err := validateChainSocks5Hop("第一层", cfg.First); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := validateChainSocks5Hop("第二层", cfg.Second); err != nil {
|
|
return nil, err
|
|
}
|
|
if cfg.LocalPort < 0 || cfg.LocalPort > 65535 {
|
|
return nil, fmt.Errorf("本地监听端口必须在 1-65535 之间")
|
|
}
|
|
cfg.First.Protocol = normalizeChainHopProtocol(cfg.First.Protocol)
|
|
cfg.Second.Protocol = normalizeChainHopProtocol(cfg.Second.Protocol)
|
|
return &cfg, nil
|
|
}
|
|
|
|
func normalizeChainHopProtocol(protocol string) string {
|
|
normalized := strings.ToLower(strings.TrimSpace(protocol))
|
|
if normalized == "" {
|
|
return "socks5"
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
func validateChainSocks5Hop(label string, hop chainSocks5Hop) error {
|
|
if strings.TrimSpace(hop.Server) == "" {
|
|
return fmt.Errorf("%s代理地址不能为空", label)
|
|
}
|
|
if hop.Port < 1 || hop.Port > 65535 {
|
|
return fmt.Errorf("%s代理端口必须在 1-65535 之间", label)
|
|
}
|
|
protocol := strings.ToLower(strings.TrimSpace(hop.Protocol))
|
|
if protocol != "" && protocol != "socks5" && protocol != "http" {
|
|
return fmt.Errorf("%s协议仅支持 http 或 socks5", label)
|
|
}
|
|
if strings.TrimSpace(hop.Password) != "" && strings.TrimSpace(hop.Username) == "" {
|
|
return fmt.Errorf("%s填写密码时请同时填写账号", label)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ParseProxyNode 解析代理节点
|
|
func ParseProxyNode(node string) (string, map[string]interface{}, error) {
|
|
src := strings.TrimSpace(node)
|
|
if src == "" {
|
|
return "", nil, fmt.Errorf("代理节点为空")
|
|
}
|
|
l := strings.ToLower(src)
|
|
if strings.HasPrefix(l, "http://") || strings.HasPrefix(l, "https://") || strings.HasPrefix(l, "socks5://") {
|
|
return src, nil, nil
|
|
}
|
|
if strings.HasPrefix(l, "clash://") || strings.Contains(l, "type:") || strings.Contains(l, "proxies:") {
|
|
outbound, standard, err := parseClashNode(src)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
if standard != "" {
|
|
return standard, nil, nil
|
|
}
|
|
if outbound != nil {
|
|
return "", outbound, nil
|
|
}
|
|
}
|
|
outbound, err := buildXrayOutbound(src)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
return "", outbound, nil
|
|
}
|
|
|
|
func buildXrayOutbound(node string) (map[string]interface{}, error) {
|
|
l := strings.ToLower(node)
|
|
if strings.HasPrefix(l, "vmess://") {
|
|
return buildOutboundVmess(node)
|
|
}
|
|
if strings.HasPrefix(l, "vless://") {
|
|
return buildOutboundVless(node)
|
|
}
|
|
if strings.HasPrefix(l, "trojan://") {
|
|
return buildOutboundTrojan(node)
|
|
}
|
|
if strings.HasPrefix(l, "ss://") {
|
|
return buildOutboundSS(node)
|
|
}
|
|
if strings.HasPrefix(l, "ssr://") {
|
|
return nil, fmt.Errorf("不支持 ShadowsocksR 协议,Xray 不支持 SSR,请使用 SS/vmess/vless/trojan")
|
|
}
|
|
if strings.HasPrefix(l, "hysteria2://") || strings.HasPrefix(l, "hysteria://") {
|
|
return buildOutboundHysteria2(node)
|
|
}
|
|
return nil, fmt.Errorf("不支持的节点协议")
|
|
}
|