This commit is contained in:
2025-06-12 02:17:31 +10:00
parent b5fcbab1a5
commit da9a17201b
61 changed files with 1362 additions and 1106 deletions

View File

@ -5,19 +5,29 @@ import (
"net/url"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseAnytls(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.AnytlsPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type AnytlsParser struct{}
func (p *AnytlsParser) GetPrefixes() []string {
return []string{"anytls://"}
}
func (p *AnytlsParser) GetType() string {
return "anytls"
}
func (p *AnytlsParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
link, err := url.Parse(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
@ -32,24 +42,24 @@ func ParseAnytls(proxy string) (model.Proxy, error) {
query := link.Query()
server := link.Hostname()
if server == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server host",
Raw: proxy,
}
}
portStr := link.Port()
if portStr == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server port",
Raw: proxy,
}
}
port, err := ParsePort(portStr)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Raw: portStr,
}
}
@ -61,8 +71,8 @@ func ParseAnytls(proxy string) (model.Proxy, error) {
}
remarks = strings.TrimSpace(remarks)
result := model.Proxy{
Type: "anytls",
result := P.Proxy{
Type: p.GetType(),
Name: remarks,
Server: server,
Port: port,
@ -72,3 +82,7 @@ func ParseAnytls(proxy string) (model.Proxy, error) {
}
return result, nil
}
func init() {
RegisterParser(&AnytlsParser{})
}

View File

@ -1,23 +0,0 @@
package parser
import (
"encoding/base64"
"strings"
)
func DecodeBase64(s string) (string, error) {
s = strings.TrimSpace(s)
if strings.Contains(s, "-") || strings.Contains(s, "_") {
s = strings.Replace(s, "-", "+", -1)
s = strings.Replace(s, "_", "/", -1)
}
if len(s)%4 != 0 {
s += strings.Repeat("=", 4-len(s)%4)
}
decodeStr, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return "", err
}
return string(decodeStr), nil
}

87
parser/common.go Normal file
View File

@ -0,0 +1,87 @@
package parser
import (
"encoding/base64"
"errors"
"strconv"
"strings"
"github.com/bestnite/sub2clash/logger"
P "github.com/bestnite/sub2clash/model/proxy"
"go.uber.org/zap"
)
func hasPrefix(proxy string, prefixes []string) bool {
hasPrefix := false
for _, prefix := range prefixes {
if strings.HasPrefix(proxy, prefix) {
hasPrefix = true
break
}
}
return hasPrefix
}
func ParsePort(portStr string) (int, error) {
port, err := strconv.Atoi(portStr)
if err != nil {
return 0, err
}
if port < 1 || port > 65535 {
return 0, errors.New("invaild port range")
}
return port, nil
}
func isLikelyBase64(s string) bool {
if len(s)%4 == 0 && strings.HasSuffix(s, "=") && !strings.Contains(strings.TrimSuffix(s, "="), "=") {
s = strings.TrimSuffix(s, "=")
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
for _, c := range s {
if !strings.ContainsRune(chars, c) {
return false
}
}
return true
}
return false
}
func DecodeBase64(s string) (string, error) {
s = strings.TrimSpace(s)
if strings.Contains(s, "-") || strings.Contains(s, "_") {
s = strings.Replace(s, "-", "+", -1)
s = strings.Replace(s, "_", "/", -1)
}
if len(s)%4 != 0 {
s += strings.Repeat("=", 4-len(s)%4)
}
decodeStr, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return "", err
}
return string(decodeStr), nil
}
func ParseProxies(proxies ...string) []P.Proxy {
var result []P.Proxy
for _, proxy := range proxies {
if proxy != "" {
var proxyItem P.Proxy
var err error
proxyItem, err = ParseProxyWithRegistry(proxy)
if err == nil {
result = append(result, proxyItem)
} else {
logger.Logger.Debug(
"parse proxy failed", zap.String("proxy", proxy), zap.Error(err),
)
}
}
}
return result
}

View File

@ -1,24 +0,0 @@
package parser
type ParseError struct {
Type ParseErrorType
Message string
Raw string
}
type ParseErrorType string
const (
ErrInvalidPrefix ParseErrorType = "invalid url prefix"
ErrInvalidStruct ParseErrorType = "invalid struct"
ErrInvalidPort ParseErrorType = "invalid port number"
ErrCannotParseParams ParseErrorType = "cannot parse query parameters"
ErrInvalidBase64 ParseErrorType = "invalid base64"
)
func (e *ParseError) Error() string {
if e.Message != "" {
return string(e.Type) + ": " + e.Message + " \"" + e.Raw + "\""
}
return string(e.Type)
}

View File

@ -6,27 +6,37 @@ import (
"strconv"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseHysteria(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.HysteriaPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type HysteriaParser struct{}
func (p *HysteriaParser) GetPrefixes() []string {
return []string{"hysteria://"}
}
func (p *HysteriaParser) GetType() string {
return "hysteria"
}
func (p *HysteriaParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
link, err := url.Parse(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
}
server := link.Hostname()
if server == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server host",
Raw: proxy,
}
@ -34,8 +44,8 @@ func ParseHysteria(proxy string) (model.Proxy, error) {
portStr := link.Port()
if portStr == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server port",
Raw: proxy,
}
@ -43,8 +53,8 @@ func ParseHysteria(proxy string) (model.Proxy, error) {
port, err := ParsePort(portStr)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Message: err.Error(),
Raw: proxy,
}
@ -70,8 +80,8 @@ func ParseHysteria(proxy string) (model.Proxy, error) {
}
remarks = strings.TrimSpace(remarks)
result := model.Proxy{
Type: "hysteria",
result := P.Proxy{
Type: p.GetType(),
Name: remarks,
Server: server,
Port: port,
@ -86,3 +96,7 @@ func ParseHysteria(proxy string) (model.Proxy, error) {
}
return result, nil
}
func init() {
RegisterParser(&HysteriaParser{})
}

View File

@ -5,20 +5,29 @@ import (
"net/url"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseHysteria2(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.Hysteria2Prefix1) &&
!strings.HasPrefix(proxy, constant.Hysteria2Prefix2) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type Hysteria2Parser struct{}
func (p *Hysteria2Parser) GetPrefixes() []string {
return []string{"hysteria2://", "hy2://"}
}
func (p *Hysteria2Parser) GetType() string {
return "hysteria2"
}
func (p *Hysteria2Parser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
link, err := url.Parse(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
@ -33,24 +42,24 @@ func ParseHysteria2(proxy string) (model.Proxy, error) {
query := link.Query()
server := link.Hostname()
if server == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server host",
Raw: proxy,
}
}
portStr := link.Port()
if portStr == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server port",
Raw: proxy,
}
}
port, err := ParsePort(portStr)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Raw: portStr,
}
}
@ -63,8 +72,8 @@ func ParseHysteria2(proxy string) (model.Proxy, error) {
}
remarks = strings.TrimSpace(remarks)
result := model.Proxy{
Type: "hysteria2",
result := P.Proxy{
Type: p.GetType(),
Name: remarks,
Server: server,
Port: port,
@ -78,3 +87,7 @@ func ParseHysteria2(proxy string) (model.Proxy, error) {
}
return result, nil
}
func init() {
RegisterParser(&Hysteria2Parser{})
}

View File

@ -1,18 +0,0 @@
package parser
import (
"errors"
"strconv"
)
func ParsePort(portStr string) (int, error) {
port, err := strconv.Atoi(portStr)
if err != nil {
return 0, err
}
if port < 1 || port > 65535 {
return 0, errors.New("invaild port range")
}
return port, nil
}

77
parser/registry.go Normal file
View File

@ -0,0 +1,77 @@
package parser
import (
"strings"
"sync"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
// ProxyParser 定义代理解析器接口
type ProxyParser interface {
// Parse 解析代理字符串并返回Proxy对象
Parse(proxy string) (P.Proxy, error)
// GetPrefix 返回支持的协议前缀(可以返回多个)
GetPrefixes() []string
// GetType 返回协议类型名称
GetType() string
}
// parserRegistry 解析器注册中心
type parserRegistry struct {
mu sync.RWMutex
parsers map[string]ProxyParser // prefix -> parser
}
var registry = &parserRegistry{
parsers: make(map[string]ProxyParser),
}
// RegisterParser 注册解析器
func RegisterParser(parser ProxyParser) {
registry.mu.Lock()
defer registry.mu.Unlock()
for _, prefix := range parser.GetPrefixes() {
registry.parsers[prefix] = parser
}
}
// GetParser 根据前缀获取解析器
func GetParser(prefix string) (ProxyParser, bool) {
registry.mu.RLock()
defer registry.mu.RUnlock()
parser, exists := registry.parsers[prefix]
return parser, exists
}
// GetAllParsers 获取所有注册的解析器
func GetAllParsers() map[string]ProxyParser {
registry.mu.RLock()
defer registry.mu.RUnlock()
result := make(map[string]ProxyParser)
for k, v := range registry.parsers {
result[k] = v
}
return result
}
// ParseProxyWithRegistry 使用注册机制解析代理
func ParseProxyWithRegistry(proxy string) (P.Proxy, error) {
proxy = strings.TrimSpace(proxy)
if proxy == "" {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidStruct, Raw: proxy, Message: "empty proxy string"}
}
// 查找匹配的解析器
for prefix, parser := range registry.parsers {
if strings.HasPrefix(proxy, prefix) {
return parser.Parse(proxy)
}
}
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy, Message: "unsupported protocol"}
}

View File

@ -5,20 +5,41 @@ import (
"net/url"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseShadowsocks(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.ShadowsocksPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
// ShadowsocksParser Shadowsocks协议解析器
type ShadowsocksParser struct{}
// GetPrefixes 返回支持的协议前缀
func (p *ShadowsocksParser) GetPrefixes() []string {
return []string{"ss://"}
}
// GetType 返回协议类型
func (p *ShadowsocksParser) GetType() string {
return "ss"
}
// Parse 解析Shadowsocks代理
func (p *ShadowsocksParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
if !strings.Contains(proxy, "@") {
s := strings.SplitN(proxy, "#", 2)
d, err := DecodeBase64(strings.TrimPrefix(s[0], "ss://"))
for _, prefix := range p.GetPrefixes() {
if strings.HasPrefix(s[0], prefix) {
s[0] = strings.TrimPrefix(s[0], prefix)
break
}
}
d, err := DecodeBase64(s[0])
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
@ -31,8 +52,8 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) {
}
link, err := url.Parse(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
@ -40,8 +61,8 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) {
server := link.Hostname()
if server == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server host",
Raw: proxy,
}
@ -49,16 +70,16 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) {
portStr := link.Port()
if portStr == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server port",
Raw: proxy,
}
}
port, err := ParsePort(portStr)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Raw: proxy,
}
}
@ -79,8 +100,8 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) {
if isLikelyBase64(password) {
password, err = DecodeBase64(password)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "password decode error",
Raw: proxy,
}
@ -93,8 +114,8 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) {
}
remarks = strings.TrimSpace(remarks)
result := model.Proxy{
Type: "ss",
result := P.Proxy{
Type: p.GetType(),
Cipher: method,
Password: password,
Server: server,
@ -105,16 +126,7 @@ func ParseShadowsocks(proxy string) (model.Proxy, error) {
return result, nil
}
func isLikelyBase64(s string) bool {
if len(s)%4 == 0 && strings.HasSuffix(s, "=") && !strings.Contains(strings.TrimSuffix(s, "="), "=") {
s = strings.TrimSuffix(s, "=")
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
for _, c := range s {
if !strings.ContainsRune(chars, c) {
return false
}
}
return true
}
return false
// 注册解析器
func init() {
RegisterParser(&ShadowsocksParser{})
}

View File

@ -5,20 +5,36 @@ import (
"strconv"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseShadowsocksR(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.ShadowsocksRPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type ShadowsocksRParser struct{}
func (p *ShadowsocksRParser) GetPrefixes() []string {
return []string{"ssr://"}
}
func (p *ShadowsocksRParser) GetType() string {
return "ssr"
}
func (p *ShadowsocksRParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
for _, prefix := range p.GetPrefixes() {
if strings.HasPrefix(proxy, prefix) {
proxy = strings.TrimPrefix(proxy, prefix)
break
}
}
proxy = strings.TrimPrefix(proxy, constant.ShadowsocksRPrefix)
proxy, err := DecodeBase64(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidBase64,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidBase64,
Raw: proxy,
}
}
@ -30,16 +46,16 @@ func ParseShadowsocksR(proxy string) (model.Proxy, error) {
obfs := parts[4]
password, err := DecodeBase64(parts[5])
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Raw: proxy,
Message: err.Error(),
}
}
port, err := ParsePort(parts[1])
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Message: err.Error(),
Raw: proxy,
}
@ -51,8 +67,8 @@ func ParseShadowsocksR(proxy string) (model.Proxy, error) {
if len(serverInfoAndParams) == 2 {
params, err := url.ParseQuery(serverInfoAndParams[1])
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrCannotParseParams,
return P.Proxy{}, &E.ParseError{
Type: E.ErrCannotParseParams,
Raw: proxy,
Message: err.Error(),
}
@ -69,17 +85,17 @@ func ParseShadowsocksR(proxy string) (model.Proxy, error) {
remarks = server + ":" + strconv.Itoa(port)
}
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Raw: proxy,
Message: err.Error(),
}
}
}
result := model.Proxy{
result := P.Proxy{
Name: remarks,
Type: "ssr",
Type: p.GetType(),
Server: server,
Port: port,
Protocol: protocol,
@ -92,3 +108,7 @@ func ParseShadowsocksR(proxy string) (model.Proxy, error) {
return result, nil
}
func init() {
RegisterParser(&ShadowsocksRParser{})
}

View File

@ -2,44 +2,56 @@ package parser
import (
"fmt"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
"net/url"
"strings"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseSocks(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.SocksPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type SocksParser struct{}
func (p *SocksParser) GetPrefixes() []string {
return []string{"socks://"}
}
func (p *SocksParser) GetType() string {
return "socks5"
}
func (p *SocksParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
link, err := url.Parse(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
}
server := link.Hostname()
if server == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server host",
Raw: proxy,
}
}
portStr := link.Port()
if portStr == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server port",
Raw: proxy,
}
}
port, err := ParsePort(portStr)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Raw: portStr,
}
}
@ -56,8 +68,8 @@ func ParseSocks(proxy string) (model.Proxy, error) {
decodeStr, err := DecodeBase64(encodeStr)
splitStr := strings.Split(decodeStr, ":")
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
@ -67,8 +79,8 @@ func ParseSocks(proxy string) (model.Proxy, error) {
password = splitStr[1]
}
}
return model.Proxy{
Type: "socks5",
return P.Proxy{
Type: p.GetType(),
Name: remarks,
Server: server,
Port: port,
@ -77,3 +89,7 @@ func ParseSocks(proxy string) (model.Proxy, error) {
}, nil
}
func init() {
RegisterParser(&SocksParser{})
}

View File

@ -5,19 +5,29 @@ import (
"net/url"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseTrojan(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.TrojanPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type TrojanParser struct{}
func (p *TrojanParser) GetPrefixes() []string {
return []string{"trojan://"}
}
func (p *TrojanParser) GetType() string {
return "trojan"
}
func (p *TrojanParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
link, err := url.Parse(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
@ -26,16 +36,16 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
password := link.User.Username()
server := link.Hostname()
if server == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server host",
Raw: proxy,
}
}
portStr := link.Port()
if portStr == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server port",
Raw: proxy,
}
@ -43,8 +53,8 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
port, err := ParsePort(portStr)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Message: err.Error(),
Raw: proxy,
}
@ -66,8 +76,8 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
alpn = nil
}
result := model.Proxy{
Type: "trojan",
result := P.Proxy{
Type: p.GetType(),
Server: server,
Port: port,
Password: password,
@ -84,7 +94,7 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
if security == "reality" {
result.TLS = true
result.Sni = sni
result.RealityOpts = model.RealityOptions{
result.RealityOpts = P.RealityOptions{
PublicKey: pbk,
ShortID: sid,
}
@ -93,7 +103,7 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
if network == "ws" {
result.Network = "ws"
result.WSOpts = model.WSOptions{
result.WSOpts = P.WSOptions{
Path: path,
Headers: map[string]string{
"Host": host,
@ -102,10 +112,14 @@ func ParseTrojan(proxy string) (model.Proxy, error) {
}
if network == "grpc" {
result.GrpcOpts = model.GrpcOptions{
result.GrpcOpts = P.GrpcOptions{
GrpcServiceName: serviceName,
}
}
return result, nil
}
func init() {
RegisterParser(&TrojanParser{})
}

View File

@ -5,19 +5,29 @@ import (
"net/url"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseVless(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.VLESSPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type VlessParser struct{}
func (p *VlessParser) GetPrefixes() []string {
return []string{"vless://"}
}
func (p *VlessParser) GetType() string {
return "vless"
}
func (p *VlessParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
link, err := url.Parse(proxy)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "url parse error",
Raw: proxy,
}
@ -25,8 +35,8 @@ func ParseVless(proxy string) (model.Proxy, error) {
server := link.Hostname()
if server == "" {
return model.Proxy{}, &ParseError{
Type: ErrInvalidStruct,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidStruct,
Message: "missing server host",
Raw: proxy,
}
@ -34,8 +44,8 @@ func ParseVless(proxy string) (model.Proxy, error) {
portStr := link.Port()
port, err := ParsePort(portStr)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Message: err.Error(),
Raw: proxy,
}
@ -58,8 +68,8 @@ func ParseVless(proxy string) (model.Proxy, error) {
}
remarks = strings.TrimSpace(remarks)
result := model.Proxy{
Type: "vless",
result := P.Proxy{
Type: p.GetType(),
Server: server,
Name: remarks,
Port: port,
@ -78,7 +88,7 @@ func ParseVless(proxy string) (model.Proxy, error) {
if security == "reality" {
result.TLS = true
result.Servername = sni
result.RealityOpts = model.RealityOptions{
result.RealityOpts = P.RealityOptions{
PublicKey: pbk,
ShortID: sid,
}
@ -87,7 +97,7 @@ func ParseVless(proxy string) (model.Proxy, error) {
if _type == "ws" {
result.Network = "ws"
result.WSOpts = model.WSOptions{
result.WSOpts = P.WSOptions{
Path: path,
}
if host != "" {
@ -98,21 +108,21 @@ func ParseVless(proxy string) (model.Proxy, error) {
if _type == "grpc" {
result.Network = "grpc"
result.GrpcOpts = model.GrpcOptions{
result.GrpcOpts = P.GrpcOptions{
GrpcServiceName: serviceName,
}
}
if _type == "http" {
result.HTTPOpts = model.HTTPOptions{}
result.HTTPOpts = P.HTTPOptions{}
result.HTTPOpts.Headers = map[string][]string{}
result.HTTPOpts.Path = strings.Split(path, ",")
hosts, err := url.QueryUnescape(host)
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrCannotParseParams,
return P.Proxy{}, &E.ParseError{
Type: E.ErrCannotParseParams,
Raw: proxy,
Message: err.Error(),
}
@ -125,3 +135,7 @@ func ParseVless(proxy string) (model.Proxy, error) {
return result, nil
}
func init() {
RegisterParser(&VlessParser{})
}

View File

@ -6,25 +6,40 @@ import (
"strconv"
"strings"
"github.com/nitezs/sub2clash/constant"
"github.com/nitezs/sub2clash/model"
E "github.com/bestnite/sub2clash/error"
P "github.com/bestnite/sub2clash/model/proxy"
)
func ParseVmess(proxy string) (model.Proxy, error) {
if !strings.HasPrefix(proxy, constant.VMessPrefix) {
return model.Proxy{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy}
type VmessParser struct{}
func (p *VmessParser) GetPrefixes() []string {
return []string{"vmess://"}
}
func (p *VmessParser) GetType() string {
return "vmess"
}
func (p *VmessParser) Parse(proxy string) (P.Proxy, error) {
if !hasPrefix(proxy, p.GetPrefixes()) {
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidPrefix, Raw: proxy}
}
proxy = strings.TrimPrefix(proxy, constant.VMessPrefix)
for _, prefix := range p.GetPrefixes() {
if strings.HasPrefix(proxy, prefix) {
proxy = strings.TrimPrefix(proxy, prefix)
break
}
}
base64, err := DecodeBase64(proxy)
if err != nil {
return model.Proxy{}, &ParseError{Type: ErrInvalidBase64, Raw: proxy, Message: err.Error()}
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidBase64, Raw: proxy, Message: err.Error()}
}
var vmess model.VmessJson
var vmess P.VmessJson
err = json.Unmarshal([]byte(base64), &vmess)
if err != nil {
return model.Proxy{}, &ParseError{Type: ErrInvalidStruct, Raw: proxy, Message: err.Error()}
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidStruct, Raw: proxy, Message: err.Error()}
}
var port int
@ -32,8 +47,8 @@ func ParseVmess(proxy string) (model.Proxy, error) {
case string:
port, err = ParsePort(vmess.Port.(string))
if err != nil {
return model.Proxy{}, &ParseError{
Type: ErrInvalidPort,
return P.Proxy{}, &E.ParseError{
Type: E.ErrInvalidPort,
Message: err.Error(),
Raw: proxy,
}
@ -47,7 +62,7 @@ func ParseVmess(proxy string) (model.Proxy, error) {
case string:
aid, err = strconv.Atoi(vmess.Aid.(string))
if err != nil {
return model.Proxy{}, &ParseError{Type: ErrInvalidStruct, Raw: proxy, Message: err.Error()}
return P.Proxy{}, &E.ParseError{Type: E.ErrInvalidStruct, Raw: proxy, Message: err.Error()}
}
case float64:
aid = int(vmess.Aid.(float64))
@ -62,9 +77,9 @@ func ParseVmess(proxy string) (model.Proxy, error) {
name = vmess.Ps
}
result := model.Proxy{
result := P.Proxy{
Name: name,
Type: "vmess",
Type: p.GetType(),
Server: vmess.Add,
Port: port,
UUID: vmess.Id,
@ -93,7 +108,7 @@ func ParseVmess(proxy string) (model.Proxy, error) {
vmess.Host = vmess.Add
}
result.Network = "ws"
result.WSOpts = model.WSOptions{
result.WSOpts = P.WSOptions{
Path: vmess.Path,
Headers: map[string]string{
"Host": vmess.Host,
@ -102,14 +117,14 @@ func ParseVmess(proxy string) (model.Proxy, error) {
}
if vmess.Net == "grpc" {
result.GrpcOpts = model.GrpcOptions{
result.GrpcOpts = P.GrpcOptions{
GrpcServiceName: vmess.Path,
}
result.Network = "grpc"
}
if vmess.Net == "h2" {
result.HTTP2Opts = model.HTTP2Options{
result.HTTP2Opts = P.HTTP2Options{
Host: strings.Split(vmess.Host, ","),
Path: vmess.Path,
}
@ -118,3 +133,7 @@ func ParseVmess(proxy string) (model.Proxy, error) {
return result, nil
}
func init() {
RegisterParser(&VmessParser{})
}