mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
296 lines
7.0 KiB
Go
296 lines
7.0 KiB
Go
package proxy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func buildOutboundVmess(node string) (map[string]interface{}, error) {
|
|
raw := strings.TrimPrefix(node, "vmess://")
|
|
decoded, err := decodeBase64String(strings.TrimSpace(raw))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("vmess 解析失败: %v", err)
|
|
}
|
|
var v struct {
|
|
Add string `json:"add"`
|
|
Port string `json:"port"`
|
|
ID string `json:"id"`
|
|
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"`
|
|
}
|
|
if err := json.Unmarshal(decoded, &v); err != nil {
|
|
return nil, fmt.Errorf("vmess 配置解析失败: %v", err)
|
|
}
|
|
p, _ := strconv.Atoi(v.Port)
|
|
out := map[string]interface{}{
|
|
"protocol": "vmess",
|
|
"tag": "proxy-out",
|
|
"settings": map[string]interface{}{
|
|
"vnext": []interface{}{
|
|
map[string]interface{}{
|
|
"address": v.Add,
|
|
"port": p,
|
|
"users": []interface{}{
|
|
map[string]interface{}{
|
|
"id": v.ID,
|
|
"security": "auto",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
stream := map[string]interface{}{}
|
|
if v.TLS == "tls" {
|
|
stream["security"] = "tls"
|
|
if v.Sni != "" {
|
|
stream["tlsSettings"] = map[string]interface{}{"serverName": v.Sni}
|
|
}
|
|
}
|
|
if v.Net == "ws" {
|
|
stream["network"] = "ws"
|
|
ws := map[string]interface{}{}
|
|
if v.Path != "" {
|
|
ws["path"] = v.Path
|
|
}
|
|
if v.Host != "" {
|
|
ws["headers"] = map[string]interface{}{"Host": v.Host}
|
|
}
|
|
if len(ws) > 0 {
|
|
stream["wsSettings"] = ws
|
|
}
|
|
}
|
|
if len(stream) > 0 {
|
|
out["streamSettings"] = stream
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func buildOutboundVless(node string) (map[string]interface{}, error) {
|
|
u, err := url.Parse(node)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("vless 解析失败: %v", err)
|
|
}
|
|
host := u.Hostname()
|
|
portStr := u.Port()
|
|
p, _ := strconv.Atoi(portStr)
|
|
id := u.User.Username()
|
|
q := u.Query()
|
|
flow := q.Get("flow")
|
|
sec := strings.ToLower(q.Get("security"))
|
|
sni := q.Get("sni")
|
|
fingerprint := firstNonEmptyQueryValue(q, "fp", "client-fingerprint", "fingerprint")
|
|
insecure := queryBool(q, "insecure", "allowInsecure")
|
|
out := map[string]interface{}{
|
|
"protocol": "vless",
|
|
"tag": "proxy-out",
|
|
"settings": map[string]interface{}{
|
|
"vnext": []interface{}{
|
|
map[string]interface{}{
|
|
"address": host,
|
|
"port": p,
|
|
"users": []interface{}{
|
|
map[string]interface{}{
|
|
"id": id,
|
|
"flow": flow,
|
|
"encryption": "none",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
stream := map[string]interface{}{}
|
|
if sec == "tls" || sec == "reality" {
|
|
stream["security"] = "tls"
|
|
tlsSettings := map[string]interface{}{}
|
|
if sni != "" {
|
|
tlsSettings["serverName"] = sni
|
|
}
|
|
if fingerprint != "" {
|
|
tlsSettings["fingerprint"] = fingerprint
|
|
}
|
|
if insecure {
|
|
tlsSettings[xrayTLSInsecurePinKey] = true
|
|
}
|
|
if len(tlsSettings) > 0 {
|
|
stream["tlsSettings"] = tlsSettings
|
|
}
|
|
}
|
|
network := q.Get("type")
|
|
if network == "" {
|
|
network = q.Get("network")
|
|
}
|
|
if network == "ws" {
|
|
stream["network"] = "ws"
|
|
ws := map[string]interface{}{}
|
|
if pth := q.Get("path"); pth != "" {
|
|
ws["path"] = pth
|
|
}
|
|
hostH := q.Get("host")
|
|
if hostH == "" {
|
|
hostH = u.Hostname()
|
|
}
|
|
if hostH != "" {
|
|
ws["headers"] = map[string]interface{}{"Host": hostH}
|
|
}
|
|
stream["wsSettings"] = ws
|
|
}
|
|
if len(stream) > 0 {
|
|
out["streamSettings"] = stream
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func buildOutboundHysteria2(node string) (map[string]interface{}, error) {
|
|
return nil, fmt.Errorf("Xray 不支持 hysteria2 协议,请使用 vless/vmess/socks5/http 格式的代理")
|
|
}
|
|
|
|
// buildOutboundTrojan 解析 trojan:// URI 格式
|
|
func buildOutboundTrojan(node string) (map[string]interface{}, error) {
|
|
u, err := url.Parse(node)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("trojan 解析失败: %v", err)
|
|
}
|
|
host := u.Hostname()
|
|
portStr := u.Port()
|
|
p, _ := strconv.Atoi(portStr)
|
|
password := u.User.Username()
|
|
q := u.Query()
|
|
sni := q.Get("sni")
|
|
if sni == "" {
|
|
sni = q.Get("peer")
|
|
}
|
|
fingerprint := firstNonEmptyQueryValue(q, "fp", "client-fingerprint", "fingerprint")
|
|
insecure := queryBool(q, "insecure", "allowInsecure")
|
|
network := q.Get("type")
|
|
|
|
out := map[string]interface{}{
|
|
"protocol": "trojan",
|
|
"tag": "proxy-out",
|
|
"settings": map[string]interface{}{
|
|
"servers": []interface{}{
|
|
map[string]interface{}{
|
|
"address": host,
|
|
"port": p,
|
|
"password": password,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
stream := map[string]interface{}{
|
|
"security": "tls",
|
|
"tlsSettings": map[string]interface{}{},
|
|
}
|
|
tlsSettings := stream["tlsSettings"].(map[string]interface{})
|
|
if sni != "" {
|
|
tlsSettings["serverName"] = sni
|
|
}
|
|
if fingerprint != "" {
|
|
tlsSettings["fingerprint"] = fingerprint
|
|
}
|
|
if insecure {
|
|
tlsSettings[xrayTLSInsecurePinKey] = true
|
|
}
|
|
if network == "ws" {
|
|
stream["network"] = "ws"
|
|
ws := map[string]interface{}{}
|
|
if pth := q.Get("path"); pth != "" {
|
|
ws["path"] = pth
|
|
}
|
|
if h := q.Get("host"); h != "" {
|
|
ws["headers"] = map[string]interface{}{"Host": h}
|
|
}
|
|
stream["wsSettings"] = ws
|
|
}
|
|
out["streamSettings"] = stream
|
|
return out, nil
|
|
}
|
|
|
|
// buildOutboundSS 解析 ss:// URI 格式
|
|
// 支持两种格式:
|
|
// 1. ss://BASE64(method:password)@host:port
|
|
// 2. ss://BASE64(method:password@host:port)
|
|
func buildOutboundSS(node string) (map[string]interface{}, error) {
|
|
raw := strings.TrimPrefix(node, "ss://")
|
|
if idx := strings.Index(raw, "#"); idx >= 0 {
|
|
raw = raw[:idx]
|
|
}
|
|
raw = strings.TrimSpace(raw)
|
|
|
|
var host, method, password string
|
|
var port int
|
|
|
|
if strings.Contains(raw, "@") {
|
|
u, err := url.Parse("ss://" + raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ss 解析失败: %v", err)
|
|
}
|
|
host = u.Hostname()
|
|
port, _ = strconv.Atoi(u.Port())
|
|
userInfo := u.User.String()
|
|
if decoded, err := decodeBase64String(userInfo); err == nil {
|
|
parts := strings.SplitN(string(decoded), ":", 2)
|
|
if len(parts) == 2 {
|
|
method = parts[0]
|
|
password = parts[1]
|
|
}
|
|
} else {
|
|
parts := strings.SplitN(userInfo, ":", 2)
|
|
if len(parts) == 2 {
|
|
method = parts[0]
|
|
password = parts[1]
|
|
}
|
|
}
|
|
} else {
|
|
decoded, err := decodeBase64String(raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ss base64 解析失败: %v", err)
|
|
}
|
|
s := string(decoded)
|
|
atIdx := strings.LastIndex(s, "@")
|
|
if atIdx < 0 {
|
|
return nil, fmt.Errorf("ss 格式错误")
|
|
}
|
|
userPart := s[:atIdx]
|
|
hostPart := s[atIdx+1:]
|
|
parts := strings.SplitN(userPart, ":", 2)
|
|
if len(parts) == 2 {
|
|
method = parts[0]
|
|
password = parts[1]
|
|
}
|
|
parsedHost, parsedPort, splitErr := splitHostPortLenient(hostPart)
|
|
if splitErr == nil {
|
|
host = parsedHost
|
|
port = parsedPort
|
|
}
|
|
}
|
|
|
|
if host == "" || port == 0 || method == "" {
|
|
return nil, fmt.Errorf("ss 节点信息不完整")
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"protocol": "shadowsocks",
|
|
"tag": "proxy-out",
|
|
"settings": map[string]interface{}{
|
|
"servers": []interface{}{
|
|
map[string]interface{}{
|
|
"address": host,
|
|
"port": port,
|
|
"method": method,
|
|
"password": password,
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
}
|