mirror of
https://github.com/nitezs/sub2clash.git
synced 2024-12-23 22:14:41 -05:00
♻️ Refactor parsers
This commit is contained in:
parent
48dece2a51
commit
ebc91d8aad
@ -76,7 +76,7 @@ func BuildSub(clashType model.ClashType, query validator.SubValidator, template
|
|||||||
err = yaml.Unmarshal(data, &sub)
|
err = yaml.Unmarshal(data, &sub)
|
||||||
var newProxies []model.Proxy
|
var newProxies []model.Proxy
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reg, _ := regexp.Compile("(ssr|ss|vmess|trojan|vless|hysteria)://")
|
reg, _ := regexp.Compile("(ssr|ss|vmess|trojan|vless|hysteria|hy2|hysteria2)://")
|
||||||
if reg.Match(data) {
|
if reg.Match(data) {
|
||||||
p := utils.ParseProxy(strings.Split(string(data), "\n")...)
|
p := utils.ParseProxy(strings.Split(string(data), "\n")...)
|
||||||
newProxies = p
|
newProxies = p
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HysteriaPrefix string = "hysteria://"
|
HysteriaPrefix string = "hysteria://"
|
||||||
Hysteria2Prefix1 string = "hysteria2://"
|
Hysteria2Prefix1 string = "hysteria2://"
|
||||||
Hysteria2Prefix2 string = "hy2://"
|
Hysteria2Prefix2 string = "hy2://"
|
||||||
ShadowsocksPrefix string = "ss://"
|
ShadowsocksPrefix string = "ss://"
|
||||||
TrojanPrefix string = "trojan://"
|
ShadowsocksRPrefix string = "ssr://"
|
||||||
VLESSPrefix string = "vless://"
|
TrojanPrefix string = "trojan://"
|
||||||
VMessPrefix string = "vmess://"
|
VLESSPrefix string = "vless://"
|
||||||
|
VMessPrefix string = "vmess://"
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,11 @@ import (
|
|||||||
|
|
||||||
func DecodeBase64(s string) (string, error) {
|
func DecodeBase64(s string) (string, error) {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
|
// url safe
|
||||||
|
if strings.Contains(s, "-") || strings.Contains(s, "_") {
|
||||||
|
s = strings.Replace(s, "-", "+", -1)
|
||||||
|
s = strings.Replace(s, "_", "/", -1)
|
||||||
|
}
|
||||||
if len(s)%4 != 0 {
|
if len(s)%4 != 0 {
|
||||||
s += strings.Repeat("=", 4-len(s)%4)
|
s += strings.Repeat("=", 4-len(s)%4)
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,86 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sub2clash/constant"
|
||||||
"sub2clash/model"
|
"sub2clash/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
//hysteria://host:port?protocol=udp&auth=123456&peer=sni.domain&insecure=1&upmbps=100&downmbps=100&alpn=hysteria&obfs=xplus&obfsParam=123456#remarks
|
|
||||||
//
|
|
||||||
//- host: hostname or IP address of the server to connect to (required)
|
|
||||||
//- port: port of the server to connect to (required)
|
|
||||||
//- protocol: protocol to use ("udp", "wechat-video", "faketcp") (optional, default: "udp")
|
|
||||||
//- auth: authentication payload (string) (optional)
|
|
||||||
//- peer: SNI for TLS (optional)
|
|
||||||
//- insecure: ignore certificate errors (optional)
|
|
||||||
//- upmbps: upstream bandwidth in Mbps (required)
|
|
||||||
//- downmbps: downstream bandwidth in Mbps (required)
|
|
||||||
//- alpn: QUIC ALPN (optional)
|
|
||||||
//- obfs: Obfuscation mode (optional, empty or "xplus")
|
|
||||||
//- obfsParam: Obfuscation password (optional)
|
|
||||||
//- remarks: remarks (optional)
|
|
||||||
|
|
||||||
func ParseHysteria(proxy string) (model.Proxy, error) {
|
func ParseHysteria(proxy string) (model.Proxy, error) {
|
||||||
// 判断是否以 hysteria:// 开头
|
if !strings.HasPrefix(proxy, constant.HysteriaPrefix) {
|
||||||
if !strings.HasPrefix(proxy, "hysteria://") {
|
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
|
||||||
return model.Proxy{}, errors.New("invalid hysteria Url")
|
|
||||||
}
|
}
|
||||||
// 分割
|
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria://"), "?", 2)
|
proxy = strings.TrimPrefix(proxy, constant.HysteriaPrefix)
|
||||||
serverInfo := strings.SplitN(parts[0], ":", 2)
|
urlParts := strings.SplitN(proxy, "?", 2)
|
||||||
|
if len(urlParts) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing character '?' in url",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverInfo := strings.SplitN(urlParts[0], ":", 2)
|
||||||
if len(serverInfo) != 2 {
|
if len(serverInfo) != 2 {
|
||||||
return model.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing server host or port",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
params, err := url.ParseQuery(parts[1])
|
server, portStr := serverInfo[0], serverInfo[1]
|
||||||
|
|
||||||
|
port, err := ParsePort(portStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidPort,
|
||||||
|
Message: err.Error(),
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
host := serverInfo[0]
|
|
||||||
port, err := strconv.Atoi(serverInfo[1])
|
params, err := url.ParseQuery(urlParts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrCannotParseParams,
|
||||||
|
Raw: proxy,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
protocol := params.Get("protocol")
|
|
||||||
auth := params.Get("auth")
|
protocol, auth, insecure, upmbps, downmbps, obfs, alpnStr := params.Get("protocol"), params.Get("auth"), params.Get("insecure"), params.Get("upmbps"), params.Get("downmbps"), params.Get("obfs"), params.Get("alpn")
|
||||||
peer := params.Get("peer")
|
insecureBool, err := strconv.ParseBool(insecure)
|
||||||
insecure := params.Get("insecure")
|
if err != nil {
|
||||||
upmbps := params.Get("upmbps")
|
insecureBool = false
|
||||||
downmbps := params.Get("downmbps")
|
|
||||||
alpn := params.Get("alpn")
|
|
||||||
obfs := params.Get("obfs")
|
|
||||||
obfsParam := params.Get("obfsParam")
|
|
||||||
remarks := ""
|
|
||||||
if strings.Contains(parts[1], "#") {
|
|
||||||
r := strings.Split(parts[1], "#")
|
|
||||||
remarks = r[len(r)-1]
|
|
||||||
} else {
|
|
||||||
remarks = serverInfo[0] + ":" + serverInfo[1]
|
|
||||||
}
|
}
|
||||||
// 返回结果
|
|
||||||
|
var alpn []string
|
||||||
|
alpnStr = strings.TrimSpace(alpnStr)
|
||||||
|
if alpnStr != "" {
|
||||||
|
alpn = strings.Split(alpnStr, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
remarks := server + ":" + portStr
|
||||||
|
if params.Get("remarks") != "" {
|
||||||
|
remarks = params.Get("remarks")
|
||||||
|
}
|
||||||
|
|
||||||
result := model.Proxy{
|
result := model.Proxy{
|
||||||
Type: "hysteria",
|
Type: "hysteria",
|
||||||
Name: remarks,
|
Name: remarks,
|
||||||
Server: host,
|
Server: server,
|
||||||
Port: port,
|
Port: port,
|
||||||
Up: upmbps,
|
Up: upmbps,
|
||||||
Down: downmbps,
|
Down: downmbps,
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
Obfs: obfs,
|
Obfs: obfs,
|
||||||
Sni: peer,
|
|
||||||
SkipCertVerify: insecure == "1",
|
SkipCertVerify: insecure == "1",
|
||||||
Alpn: strings.Split(alpn, ","),
|
Alpn: alpn,
|
||||||
ObfsParam: obfsParam,
|
|
||||||
Protocol: protocol,
|
Protocol: protocol,
|
||||||
|
AllowInsecure: insecureBool,
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,89 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sub2clash/constant"
|
||||||
"sub2clash/model"
|
"sub2clash/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// hysteria2://letmein@example.com/?insecure=1&obfs=salamander&obfs-password=gawrgura&pinSHA256=deadbeef&sni=real.example.com#name
|
|
||||||
|
|
||||||
func ParseHysteria2(proxy string) (model.Proxy, error) {
|
func ParseHysteria2(proxy string) (model.Proxy, error) {
|
||||||
// 判断是否以 hysteria2:// 开头
|
if !strings.HasPrefix(proxy, constant.Hysteria2Prefix1) &&
|
||||||
if !strings.HasPrefix(proxy, "hysteria2://") && !strings.HasPrefix(proxy, "hy2://") {
|
!strings.HasPrefix(proxy, constant.Hysteria2Prefix2) {
|
||||||
return model.Proxy{}, errors.New("invalid hysteria2 Url")
|
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
|
||||||
}
|
}
|
||||||
// 分割
|
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria2://"), "@", 2)
|
proxy = strings.TrimPrefix(proxy, constant.Hysteria2Prefix1)
|
||||||
// 分割
|
proxy = strings.TrimPrefix(proxy, constant.Hysteria2Prefix2)
|
||||||
serverInfo := strings.SplitN(parts[1], "/?", 2)
|
urlParts := strings.SplitN(proxy, "@", 2)
|
||||||
|
if len(urlParts) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing character '@' in url",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
password := urlParts[0]
|
||||||
|
|
||||||
|
serverInfo := strings.SplitN(urlParts[1], "/?", 2)
|
||||||
|
if len(serverInfo) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing params in url",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paramStr := serverInfo[1]
|
||||||
|
|
||||||
serverAndPort := strings.SplitN(serverInfo[0], ":", 2)
|
serverAndPort := strings.SplitN(serverInfo[0], ":", 2)
|
||||||
|
var server string
|
||||||
|
var portStr string
|
||||||
if len(serverAndPort) == 1 {
|
if len(serverAndPort) == 1 {
|
||||||
serverAndPort = append(serverAndPort, "443")
|
portStr = "443"
|
||||||
} else if len(serverAndPort) != 2 {
|
} else if len(serverAndPort) == 2 {
|
||||||
return model.Proxy{}, errors.New("invalid hysteria2 Url")
|
server, portStr = serverAndPort[0], serverAndPort[1]
|
||||||
}
|
|
||||||
params, err := url.ParseQuery(serverInfo[1])
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, errors.New("invalid hysteria2 Url")
|
|
||||||
}
|
|
||||||
// 获取端口
|
|
||||||
port, err := strconv.Atoi(serverAndPort[1])
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, errors.New("invalid hysteria2 Url")
|
|
||||||
}
|
|
||||||
name := ""
|
|
||||||
if strings.Contains(proxy, "#") {
|
|
||||||
splitResult := strings.Split(proxy, "#")
|
|
||||||
name, _ = url.QueryUnescape(splitResult[len(splitResult)-1])
|
|
||||||
} else {
|
} else {
|
||||||
name = strings.Join(serverAndPort, ":")
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing server host or port",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 返回结果
|
|
||||||
|
port, err := ParsePort(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidPort,
|
||||||
|
Message: err.Error(),
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := url.ParseQuery(paramStr)
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrCannotParseParams,
|
||||||
|
Raw: proxy,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remarks, network, obfs, obfsPassword, pinSHA256, insecure, sni := params.Get("name"), params.Get("network"), params.Get("obfs"), params.Get("obfs-password"), params.Get("pinSHA256"), params.Get("insecure"), params.Get("sni")
|
||||||
|
enableTLS := pinSHA256 != ""
|
||||||
|
insecureBool := insecure == "1"
|
||||||
|
|
||||||
result := model.Proxy{
|
result := model.Proxy{
|
||||||
Type: "hysteria2",
|
Type: "hysteria2",
|
||||||
Name: name,
|
Name: remarks,
|
||||||
Server: serverAndPort[0],
|
Server: server,
|
||||||
Port: port,
|
Port: port,
|
||||||
Password: parts[0],
|
Password: password,
|
||||||
Obfs: params.Get("obfs"),
|
Obfs: obfs,
|
||||||
ObfsParam: params.Get("obfs-password"),
|
ObfsParam: obfsPassword,
|
||||||
Sni: params.Get("sni"),
|
Sni: sni,
|
||||||
SkipCertVerify: params.Get("insecure") == "1",
|
SkipCertVerify: insecureBool,
|
||||||
|
TLS: enableTLS,
|
||||||
|
Network: network,
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -8,16 +9,10 @@ func ParsePort(portStr string) (int, error) {
|
|||||||
port, err := strconv.Atoi(portStr)
|
port, err := strconv.Atoi(portStr)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &ParseError{
|
return 0, err
|
||||||
Type: ErrInvalidPort,
|
|
||||||
Message: portStr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if port < 1 || port > 65535 {
|
if port < 1 || port > 65535 {
|
||||||
return 0, &ParseError{
|
return 0, errors.New("invaild port range")
|
||||||
Type: ErrInvalidPort,
|
|
||||||
Message: portStr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return port, nil
|
return port, nil
|
||||||
}
|
}
|
||||||
|
91
parser/shadowsocks.go
Normal file
91
parser/shadowsocks.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sub2clash/constant"
|
||||||
|
"sub2clash/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseShadowsocks(proxy string) (model.Proxy, error) {
|
||||||
|
if !strings.HasPrefix(proxy, constant.ShadowsocksPrefix) {
|
||||||
|
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = strings.TrimPrefix(proxy, constant.ShadowsocksPrefix)
|
||||||
|
urlParts := strings.SplitN(proxy, "@", 2)
|
||||||
|
if len(urlParts) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing character '@' in url",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverAndPort []string
|
||||||
|
if !strings.Contains(urlParts[0], ":") {
|
||||||
|
decoded, err := DecodeBase64(urlParts[0])
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "invalid base64 encoded",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
urlParts[0] = decoded
|
||||||
|
}
|
||||||
|
credentials := strings.SplitN(urlParts[0], ":", 2)
|
||||||
|
if len(credentials) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing server host or port",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
method, password := credentials[0], credentials[1]
|
||||||
|
|
||||||
|
serverInfo := strings.SplitN(urlParts[1], "#", 2)
|
||||||
|
serverAndPort = strings.SplitN(serverInfo[0], ":", 2)
|
||||||
|
server, portStr := serverAndPort[0], serverAndPort[1]
|
||||||
|
if len(serverInfo) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing server host or port",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
port, err := ParsePort(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidPort,
|
||||||
|
Message: err.Error(),
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var remarks string
|
||||||
|
if len(serverInfo) == 2 {
|
||||||
|
unescape, err := url.QueryUnescape(serverInfo[1])
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "cannot unescape remarks",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remarks = strings.TrimSpace(unescape)
|
||||||
|
} else {
|
||||||
|
remarks = strings.TrimSpace(server + ":" + portStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := model.Proxy{
|
||||||
|
Type: "ss",
|
||||||
|
Cipher: method,
|
||||||
|
Password: password,
|
||||||
|
Server: server,
|
||||||
|
Port: port,
|
||||||
|
Name: remarks,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
86
parser/shadowsocksr.go
Normal file
86
parser/shadowsocksr.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sub2clash/constant"
|
||||||
|
"sub2clash/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseShadowsocksR(proxy string) (model.Proxy, error) {
|
||||||
|
if !strings.HasPrefix(proxy, constant.ShadowsocksRPrefix) {
|
||||||
|
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = strings.TrimPrefix(proxy, constant.ShadowsocksRPrefix)
|
||||||
|
proxy, err := DecodeBase64(proxy)
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidBase64,
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serverInfoAndParams := strings.SplitN(proxy, "/?", 2)
|
||||||
|
parts := strings.Split(serverInfoAndParams[0], ":")
|
||||||
|
server := parts[0]
|
||||||
|
protocol := parts[2]
|
||||||
|
method := parts[3]
|
||||||
|
obfs := parts[4]
|
||||||
|
password := parts[5]
|
||||||
|
port, err := ParsePort(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidPort,
|
||||||
|
Message: err.Error(),
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var obfsParam string
|
||||||
|
var protoParam string
|
||||||
|
var remarks string
|
||||||
|
if len(serverInfoAndParams) == 2 {
|
||||||
|
params, err := url.ParseQuery(serverInfoAndParams[1])
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrCannotParseParams,
|
||||||
|
Raw: proxy,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if params.Get("obfsparam") != "" {
|
||||||
|
obfsParam, err = DecodeBase64(params.Get("obfsparam"))
|
||||||
|
}
|
||||||
|
if params.Get("protoparam") != "" {
|
||||||
|
protoParam, err = DecodeBase64(params.Get("protoparam"))
|
||||||
|
}
|
||||||
|
if params.Get("remarks") != "" {
|
||||||
|
remarks, err = DecodeBase64(params.Get("remarks"))
|
||||||
|
} else {
|
||||||
|
remarks = server + ":" + strconv.Itoa(port)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Raw: proxy,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := model.Proxy{
|
||||||
|
Name: remarks,
|
||||||
|
Type: "ssr",
|
||||||
|
Server: server,
|
||||||
|
Port: port,
|
||||||
|
Protocol: protocol,
|
||||||
|
Cipher: method,
|
||||||
|
Obfs: obfs,
|
||||||
|
Password: password,
|
||||||
|
ObfsParam: obfsParam,
|
||||||
|
ProtocolParam: protoParam,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
67
parser/ss.go
67
parser/ss.go
@ -1,67 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sub2clash/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseSS 解析 SS(Shadowsocks)Url
|
|
||||||
func ParseSS(proxy string) (model.Proxy, error) {
|
|
||||||
// 判断是否以 ss:// 开头
|
|
||||||
if !strings.HasPrefix(proxy, "ss://") {
|
|
||||||
return model.Proxy{}, errors.New("invalid ss Url")
|
|
||||||
}
|
|
||||||
// 分割
|
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "ss://"), "@", 2)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return model.Proxy{}, errors.New("invalid ss Url")
|
|
||||||
}
|
|
||||||
if !strings.Contains(parts[0], ":") {
|
|
||||||
// 解码
|
|
||||||
decoded, err := DecodeBase64(parts[0])
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, errors.New("invalid ss Url" + err.Error())
|
|
||||||
}
|
|
||||||
parts[0] = decoded
|
|
||||||
}
|
|
||||||
credentials := strings.SplitN(parts[0], ":", 2)
|
|
||||||
if len(credentials) != 2 {
|
|
||||||
return model.Proxy{}, errors.New("invalid ss Url")
|
|
||||||
}
|
|
||||||
// 分割
|
|
||||||
serverInfo := strings.SplitN(parts[1], "#", 2)
|
|
||||||
serverAndPort := strings.SplitN(serverInfo[0], ":", 2)
|
|
||||||
if len(serverAndPort) != 2 {
|
|
||||||
return model.Proxy{}, errors.New("invalid ss Url")
|
|
||||||
}
|
|
||||||
// 转换端口字符串为数字
|
|
||||||
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, errors.New("invalid ss Url" + err.Error())
|
|
||||||
}
|
|
||||||
// 返回结果
|
|
||||||
result := model.Proxy{
|
|
||||||
Type: "ss",
|
|
||||||
Cipher: strings.TrimSpace(credentials[0]),
|
|
||||||
Password: strings.TrimSpace(credentials[1]),
|
|
||||||
Server: strings.TrimSpace(serverAndPort[0]),
|
|
||||||
Port: port,
|
|
||||||
UDP: true,
|
|
||||||
Name: serverAndPort[0],
|
|
||||||
}
|
|
||||||
// 如果有节点名称
|
|
||||||
if len(serverInfo) == 2 {
|
|
||||||
unescape, err := url.QueryUnescape(serverInfo[1])
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, errors.New("invalid ss Url" + err.Error())
|
|
||||||
}
|
|
||||||
result.Name = strings.TrimSpace(unescape)
|
|
||||||
} else {
|
|
||||||
result.Name = strings.TrimSpace(serverAndPort[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sub2clash/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseShadowsocksR(proxy string) (model.Proxy, error) {
|
|
||||||
// 判断是否以 ssr:// 开头
|
|
||||||
if !strings.HasPrefix(proxy, "ssr://") {
|
|
||||||
return model.Proxy{}, fmt.Errorf("invalid ssr Url")
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
proxy = strings.TrimPrefix(proxy, "ssr://")
|
|
||||||
if !strings.Contains(proxy, ":") {
|
|
||||||
proxy, err = DecodeBase64(strings.TrimPrefix(proxy, "ssr://"))
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 分割
|
|
||||||
detailsAndParams := strings.SplitN(proxy, "/?", 2)
|
|
||||||
parts := strings.Split(detailsAndParams[0], ":")
|
|
||||||
params, err := url.ParseQuery(detailsAndParams[1])
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, err
|
|
||||||
}
|
|
||||||
// 处理端口
|
|
||||||
port, err := strconv.Atoi(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, err
|
|
||||||
}
|
|
||||||
var obfsParam string
|
|
||||||
var protoParam string
|
|
||||||
var remarks string
|
|
||||||
if params.Get("obfsparam") != "" {
|
|
||||||
obfsParam, err = DecodeBase64(params.Get("obfsparam"))
|
|
||||||
}
|
|
||||||
if params.Get("protoparam") != "" {
|
|
||||||
protoParam, err = DecodeBase64(params.Get("protoparam"))
|
|
||||||
}
|
|
||||||
if params.Get("remarks") != "" {
|
|
||||||
remarks, err = DecodeBase64(params.Get("remarks"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := model.Proxy{
|
|
||||||
Name: remarks,
|
|
||||||
Type: "ssr",
|
|
||||||
Server: parts[0],
|
|
||||||
Port: port,
|
|
||||||
Protocol: parts[2],
|
|
||||||
Cipher: parts[3],
|
|
||||||
Obfs: parts[4],
|
|
||||||
Password: parts[5],
|
|
||||||
ObfsParam: obfsParam,
|
|
||||||
ProtocolParam: protoParam,
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Name == "" {
|
|
||||||
result.Name = result.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
@ -55,7 +55,11 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
|
|||||||
|
|
||||||
port, err := ParsePort(portStr)
|
port, err := ParsePort(portStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, err
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidPort,
|
||||||
|
Message: err.Error(),
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remarks := ""
|
remarks := ""
|
||||||
@ -112,16 +116,13 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if network == "http" {
|
// if network == "http" {
|
||||||
result.HTTP2Opts = model.HTTP2Options{
|
// // 未查到相关支持文档
|
||||||
Host: []string{host},
|
// }
|
||||||
Path: path,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if network == "quic" {
|
// if network == "quic" {
|
||||||
// 未查到相关支持文档
|
// // 未查到相关支持文档
|
||||||
}
|
// }
|
||||||
|
|
||||||
if network == "grpc" {
|
if network == "grpc" {
|
||||||
result.GrpcOpts = model.GrpcOptions{
|
result.GrpcOpts = model.GrpcOptions{
|
||||||
|
188
parser/vless.go
188
parser/vless.go
@ -1,92 +1,156 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sub2clash/constant"
|
||||||
"sub2clash/model"
|
"sub2clash/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseVless(proxy string) (model.Proxy, error) {
|
func ParseVless(proxy string) (model.Proxy, error) {
|
||||||
// 判断是否以 vless:// 开头
|
if !strings.HasPrefix(proxy, constant.VLESSPrefix) {
|
||||||
if !strings.HasPrefix(proxy, "vless://") {
|
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
|
||||||
return model.Proxy{}, fmt.Errorf("invalid vless Url")
|
|
||||||
}
|
}
|
||||||
// 分割
|
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "vless://"), "@", 2)
|
urlParts := strings.SplitN(strings.TrimPrefix(proxy, constant.VLESSPrefix), "@", 2)
|
||||||
if len(parts) != 2 {
|
if len(urlParts) != 2 {
|
||||||
return model.Proxy{}, fmt.Errorf("invalid vless Url")
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing character '@' in url",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 分割
|
|
||||||
serverInfo := strings.SplitN(parts[1], "#", 2)
|
serverInfo := strings.SplitN(urlParts[1], "#", 2)
|
||||||
serverAndPortAndParams := strings.SplitN(serverInfo[0], "?", 2)
|
serverAndPortAndParams := strings.SplitN(serverInfo[0], "?", 2)
|
||||||
|
if len(serverAndPortAndParams) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing character '?' in url",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serverAndPort := strings.SplitN(serverAndPortAndParams[0], ":", 2)
|
serverAndPort := strings.SplitN(serverAndPortAndParams[0], ":", 2)
|
||||||
|
if len(serverAndPort) != 2 {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidStruct,
|
||||||
|
Message: "missing server host or port",
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server, portStr := serverAndPort[0], serverAndPort[1]
|
||||||
|
port, err := ParsePort(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidPort,
|
||||||
|
Message: err.Error(),
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
params, err := url.ParseQuery(serverAndPortAndParams[1])
|
params, err := url.ParseQuery(serverAndPortAndParams[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, err
|
return model.Proxy{}, &ParseError{
|
||||||
}
|
Type: ErrCannotParseParams,
|
||||||
if len(serverAndPort) != 2 {
|
Raw: proxy,
|
||||||
return model.Proxy{}, fmt.Errorf("invalid vless")
|
Message: err.Error(),
|
||||||
}
|
|
||||||
// 处理端口
|
|
||||||
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
|
||||||
if err != nil {
|
|
||||||
return model.Proxy{}, err
|
|
||||||
}
|
|
||||||
// 返回结果
|
|
||||||
result := model.Proxy{
|
|
||||||
Type: "vless",
|
|
||||||
Server: strings.TrimSpace(serverAndPort[0]),
|
|
||||||
Port: port,
|
|
||||||
UUID: strings.TrimSpace(parts[0]),
|
|
||||||
UDP: true,
|
|
||||||
Sni: params.Get("sni"),
|
|
||||||
Network: params.Get("type"),
|
|
||||||
Flow: params.Get("flow"),
|
|
||||||
ClientFingerprint: params.Get("fp"),
|
|
||||||
Servername: params.Get("sni"),
|
|
||||||
}
|
|
||||||
if params.Get("alpn") != "" {
|
|
||||||
result.Alpn = strings.Split(params.Get("alpn"), ",")
|
|
||||||
}
|
|
||||||
if params.Get("security") == "reality" {
|
|
||||||
result.TLS = true
|
|
||||||
result.RealityOpts = model.RealityOptions{
|
|
||||||
PublicKey: params.Get("pbk"),
|
|
||||||
ShortID: params.Get("sid"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "ws" {
|
|
||||||
result.TLS = true
|
remarks := ""
|
||||||
result.WSOpts = model.WSOptions{
|
|
||||||
Path: params.Get("path"),
|
|
||||||
Headers: map[string]string{
|
|
||||||
"Host": params.Get("host"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if params.Get("type") == "grpc" {
|
|
||||||
result.TLS = true
|
|
||||||
result.GrpcOpts = model.GrpcOptions{
|
|
||||||
GrpcServiceName: params.Get("serviceName"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果有节点名称
|
|
||||||
if len(serverInfo) == 2 {
|
if len(serverInfo) == 2 {
|
||||||
if strings.Contains(serverInfo[1], "|") {
|
if strings.Contains(serverInfo[1], "|") {
|
||||||
result.Name = strings.SplitN(serverInfo[1], "|", 2)[1]
|
remarks = strings.SplitN(serverInfo[1], "|", 2)[1]
|
||||||
} else {
|
} else {
|
||||||
result.Name, err = url.QueryUnescape(serverInfo[1])
|
remarks, err = url.QueryUnescape(serverInfo[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, err
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrCannotParseParams,
|
||||||
|
Raw: proxy,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.Name, err = url.QueryUnescape(serverAndPort[0])
|
remarks, err = url.QueryUnescape(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, err
|
return model.Proxy{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uuid := strings.TrimSpace(urlParts[0])
|
||||||
|
flow, security, alpnStr, sni, insecure, fp, pbk, sid, path, host, serviceName, _type := params.Get("flow"), params.Get("security"), params.Get("alpn"), params.Get("sni"), params.Get("allowInsecure"), params.Get("fp"), params.Get("pbk"), params.Get("sid"), params.Get("path"), params.Get("host"), params.Get("serviceName"), params.Get("type")
|
||||||
|
|
||||||
|
// enableUTLS := fp != ""
|
||||||
|
insecureBool := insecure == "1"
|
||||||
|
var alpn []string
|
||||||
|
if strings.Contains(alpnStr, ",") {
|
||||||
|
alpn = strings.Split(alpnStr, ",")
|
||||||
|
} else {
|
||||||
|
alpn = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := model.Proxy{
|
||||||
|
Type: "vless",
|
||||||
|
Server: server,
|
||||||
|
Name: remarks,
|
||||||
|
Port: port,
|
||||||
|
UUID: uuid,
|
||||||
|
Flow: flow,
|
||||||
|
}
|
||||||
|
|
||||||
|
if security == "tls" {
|
||||||
|
result.TLS = true
|
||||||
|
result.Alpn = alpn
|
||||||
|
result.Sni = sni
|
||||||
|
result.AllowInsecure = insecureBool
|
||||||
|
result.Fingerprint = fp
|
||||||
|
}
|
||||||
|
|
||||||
|
if security == "reality" {
|
||||||
|
result.TLS = true
|
||||||
|
result.RealityOpts = model.RealityOptions{
|
||||||
|
PublicKey: pbk,
|
||||||
|
ShortID: sid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _type == "ws" {
|
||||||
|
result.Network = "ws"
|
||||||
|
result.WSOpts = model.WSOptions{
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
if host != "" {
|
||||||
|
result.WSOpts.Headers = make(map[string]string)
|
||||||
|
result.WSOpts.Headers["Host"] = host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if _type == "quic" {
|
||||||
|
// // 未查到相关支持文档
|
||||||
|
// }
|
||||||
|
|
||||||
|
if _type == "grpc" {
|
||||||
|
result.Network = "grpc"
|
||||||
|
result.Servername = serviceName
|
||||||
|
}
|
||||||
|
|
||||||
|
if _type == "http" {
|
||||||
|
hosts, err := url.QueryUnescape(host)
|
||||||
|
if err != nil {
|
||||||
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrCannotParseParams,
|
||||||
|
Raw: proxy,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.Network = "http"
|
||||||
|
result.HTTPOpts = model.HTTPOptions{
|
||||||
|
Headers: map[string][]string{"Host": strings.Split(hosts, ",")},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
107
parser/vmess.go
107
parser/vmess.go
@ -2,83 +2,96 @@ package parser
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sub2clash/constant"
|
||||||
"sub2clash/model"
|
"sub2clash/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseVmess(proxy string) (model.Proxy, error) {
|
func ParseVmess(proxy string) (model.Proxy, error) {
|
||||||
// 判断是否以 vmess:// 开头
|
if !strings.HasPrefix(proxy, constant.VMessPrefix) {
|
||||||
if !strings.HasPrefix(proxy, "vmess://") {
|
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
|
||||||
return model.Proxy{}, errors.New("invalid vmess url")
|
|
||||||
}
|
}
|
||||||
// 解码
|
|
||||||
base64, err := DecodeBase64(strings.TrimPrefix(proxy, "vmess://"))
|
proxy = strings.TrimPrefix(proxy, constant.VMessPrefix)
|
||||||
|
base64, err := DecodeBase64(proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Proxy{}, &ParseError{Type: ErrInvalidBase64, Raw: proxy, Message: err.Error()}
|
||||||
}
|
}
|
||||||
// 解析
|
|
||||||
var vmess model.VmessJson
|
var vmess model.VmessJson
|
||||||
err = json.Unmarshal([]byte(base64), &vmess)
|
err = json.Unmarshal([]byte(base64), &vmess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Proxy{}, &ParseError{Type: ErrInvalidStruct, Raw: proxy, Message: err.Error()}
|
||||||
}
|
}
|
||||||
// 解析端口
|
|
||||||
port := 0
|
var port int
|
||||||
switch vmess.Port.(type) {
|
switch vmess.Port.(type) {
|
||||||
case string:
|
case string:
|
||||||
port, err = strconv.Atoi(vmess.Port.(string))
|
port, err = ParsePort(vmess.Port.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Proxy{}, &ParseError{
|
||||||
|
Type: ErrInvalidPort,
|
||||||
|
Message: err.Error(),
|
||||||
|
Raw: proxy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
port = int(vmess.Port.(float64))
|
port = int(vmess.Port.(float64))
|
||||||
}
|
}
|
||||||
// 解析Aid
|
|
||||||
aid := 0
|
aid := 0
|
||||||
switch vmess.Aid.(type) {
|
switch vmess.Aid.(type) {
|
||||||
case string:
|
case string:
|
||||||
aid, err = strconv.Atoi(vmess.Aid.(string))
|
aid, err = strconv.Atoi(vmess.Aid.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Proxy{}, &ParseError{Type: ErrInvalidStruct, Raw: proxy, Message: err.Error()}
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
aid = int(vmess.Aid.(float64))
|
aid = int(vmess.Aid.(float64))
|
||||||
}
|
}
|
||||||
// 设置默认值
|
|
||||||
if vmess.Scy == "" {
|
if vmess.Scy == "" {
|
||||||
vmess.Scy = "auto"
|
vmess.Scy = "auto"
|
||||||
}
|
}
|
||||||
if vmess.Net == "ws" && vmess.Path == "" {
|
|
||||||
vmess.Path = "/"
|
|
||||||
}
|
|
||||||
if vmess.Net == "ws" && vmess.Host == "" {
|
|
||||||
vmess.Host = vmess.Add
|
|
||||||
}
|
|
||||||
name, err := url.QueryUnescape(vmess.Ps)
|
name, err := url.QueryUnescape(vmess.Ps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
name = vmess.Ps
|
name = vmess.Ps
|
||||||
}
|
}
|
||||||
// 返回结果
|
|
||||||
result := model.Proxy{
|
result := model.Proxy{
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: "vmess",
|
Type: "vmess",
|
||||||
Server: vmess.Add,
|
Server: vmess.Add,
|
||||||
Port: port,
|
Port: port,
|
||||||
UUID: vmess.Id,
|
UUID: vmess.Id,
|
||||||
AlterID: aid,
|
AlterID: aid,
|
||||||
Cipher: vmess.Scy,
|
Cipher: vmess.Scy,
|
||||||
UDP: true,
|
|
||||||
TLS: vmess.Tls == "tls",
|
|
||||||
Fingerprint: vmess.Fp,
|
|
||||||
ClientFingerprint: "chrome",
|
|
||||||
SkipCertVerify: true,
|
|
||||||
Servername: vmess.Add,
|
|
||||||
Network: vmess.Net,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if vmess.Tls == "tls" {
|
||||||
|
var alpn []string
|
||||||
|
if strings.Contains(vmess.Alpn, ",") {
|
||||||
|
alpn = strings.Split(vmess.Alpn, ",")
|
||||||
|
} else {
|
||||||
|
alpn = nil
|
||||||
|
}
|
||||||
|
result.TLS = true
|
||||||
|
result.Fingerprint = vmess.Fp
|
||||||
|
result.Alpn = alpn
|
||||||
|
result.Servername = vmess.Sni
|
||||||
|
}
|
||||||
|
|
||||||
if vmess.Net == "ws" {
|
if vmess.Net == "ws" {
|
||||||
|
if vmess.Path == "" {
|
||||||
|
vmess.Path = "/"
|
||||||
|
}
|
||||||
|
if vmess.Host == "" {
|
||||||
|
vmess.Host = vmess.Add
|
||||||
|
}
|
||||||
|
result.Network = "ws"
|
||||||
result.WSOpts = model.WSOptions{
|
result.WSOpts = model.WSOptions{
|
||||||
Path: vmess.Path,
|
Path: vmess.Path,
|
||||||
Headers: map[string]string{
|
Headers: map[string]string{
|
||||||
@ -86,5 +99,25 @@ func ParseVmess(proxy string) (model.Proxy, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if vmess.Net == "quic" {
|
||||||
|
// // 未查到相关支持文档
|
||||||
|
// }
|
||||||
|
|
||||||
|
if vmess.Net == "grpc" {
|
||||||
|
result.GrpcOpts = model.GrpcOptions{
|
||||||
|
GrpcServiceName: vmess.Path,
|
||||||
|
}
|
||||||
|
result.Network = "grpc"
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmess.Net == "h2" {
|
||||||
|
result.HTTP2Opts = model.HTTP2Options{
|
||||||
|
Host: strings.Split(vmess.Host, ","),
|
||||||
|
Path: vmess.Path,
|
||||||
|
}
|
||||||
|
result.Network = "h2"
|
||||||
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sub2clash/parser"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHy2Parser(t *testing.T) {
|
func TestParser(t *testing.T) {
|
||||||
res, err := parser.ParseTrojan("trojan://Abse64hhjewrs@test.com:8443?type=ws&path=%2Fx&host=test.com&security=tls&fp=&alpn=http%2F1.1&sni=test.com#test")
|
// res, err := parser.ParseTrojan("trojan://Abse64hhjewrs@test.com:8443?type=ws&path=%2Fx&host=test.com&security=tls&fp=&alpn=http%2F1.1&sni=test.com#test")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Log(err.Error())
|
// t.Log(err.Error())
|
||||||
t.Fail()
|
// t.Fail()
|
||||||
}
|
// }
|
||||||
bytes, err := yaml.Marshal(res)
|
// bytes, err := yaml.Marshal(res)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Log(err.Error())
|
// t.Log(err.Error())
|
||||||
t.Fail()
|
// t.Fail()
|
||||||
}
|
// }
|
||||||
t.Log(string(bytes))
|
// t.Log(string(bytes))
|
||||||
|
t.Log(strings.SplitN("123456", "/?", 2))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"sub2clash/constant"
|
||||||
"sub2clash/logger"
|
"sub2clash/logger"
|
||||||
"sub2clash/model"
|
"sub2clash/model"
|
||||||
"sub2clash/parser"
|
"sub2clash/parser"
|
||||||
@ -106,25 +107,25 @@ func ParseProxy(proxies ...string) []model.Proxy {
|
|||||||
var proxyItem model.Proxy
|
var proxyItem model.Proxy
|
||||||
var err error
|
var err error
|
||||||
// 解析节点
|
// 解析节点
|
||||||
if strings.HasPrefix(proxy, "ss://") {
|
if strings.HasPrefix(proxy, constant.ShadowsocksPrefix) {
|
||||||
proxyItem, err = parser.ParseSS(proxy)
|
proxyItem, err = parser.ParseShadowsocks(proxy)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(proxy, "trojan://") {
|
if strings.HasPrefix(proxy, constant.TrojanPrefix) {
|
||||||
proxyItem, err = parser.ParseTrojan(proxy)
|
proxyItem, err = parser.ParseTrojan(proxy)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(proxy, "vmess://") {
|
if strings.HasPrefix(proxy, constant.VMessPrefix) {
|
||||||
proxyItem, err = parser.ParseVmess(proxy)
|
proxyItem, err = parser.ParseVmess(proxy)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(proxy, "vless://") {
|
if strings.HasPrefix(proxy, constant.VLESSPrefix) {
|
||||||
proxyItem, err = parser.ParseVless(proxy)
|
proxyItem, err = parser.ParseVless(proxy)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(proxy, "ssr://") {
|
if strings.HasPrefix(proxy, constant.ShadowsocksRPrefix) {
|
||||||
proxyItem, err = parser.ParseShadowsocksR(proxy)
|
proxyItem, err = parser.ParseShadowsocksR(proxy)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(proxy, "hysteria2://") || strings.HasPrefix(proxy, "hy2://") {
|
if strings.HasPrefix(proxy, constant.Hysteria2Prefix1) || strings.HasPrefix(proxy, constant.Hysteria2Prefix2) {
|
||||||
proxyItem, err = parser.ParseHysteria2(proxy)
|
proxyItem, err = parser.ParseHysteria2(proxy)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(proxy, "hysteria://") {
|
if strings.HasPrefix(proxy, constant.HysteriaPrefix) {
|
||||||
proxyItem, err = parser.ParseHysteria(proxy)
|
proxyItem, err = parser.ParseHysteria(proxy)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user