Browse Source

add vmess

zu1k 5 years ago
parent
commit
ddc6e73373
8 changed files with 280 additions and 41 deletions
  1. 4 2
      app/cache.go
  2. 6 7
      app/task.go
  3. 10 3
      getter/tgchannel.go
  4. 17 11
      provider/clash.go
  5. 0 2
      proxy/base.go
  6. 12 16
      proxy/shadowsocksr.go
  7. 218 0
      proxy/vmess.go
  8. 13 0
      proxy/vmess_test.go

+ 4 - 2
app/cache.go

@@ -1,11 +1,11 @@
 package app
 
 import (
+	"log"
 	"time"
 
-	"github.com/zu1k/proxypool/proxy"
-
 	"github.com/patrickmn/go-cache"
+	"github.com/zu1k/proxypool/proxy"
 )
 
 var c = cache.New(cache.NoExpiration, 10*time.Minute)
@@ -13,8 +13,10 @@ var c = cache.New(cache.NoExpiration, 10*time.Minute)
 func GetProxies() []proxy.Proxy {
 	result, found := c.Get("proxies")
 	if found {
+		log.Println(len(result.([]proxy.Proxy)))
 		return result.([]proxy.Proxy)
 	}
+	log.Println("Cache not found")
 	return nil
 }
 

+ 6 - 7
app/task.go

@@ -1,21 +1,20 @@
 package app
 
 import (
-	"fmt"
-
 	"github.com/zu1k/proxypool/getter"
 	"github.com/zu1k/proxypool/proxy"
 )
 
 func CrawlTGChannel() {
 	node := make([]proxy.Proxy, 0)
-	node = append(node, getter.NewTGSsrlistGetter("https://t.me/s/ssrList", 200).Get()...)
-	node = append(node, getter.NewTGSsrlistGetter("https://t.me/s/SSRSUB", 200).Get()...)
-	node = append(node, getter.NewTGSsrlistGetter("https://t.me/s/FreeSSRNode", 200).Get()...)
-	node = append(node, getter.NewTGSsrlistGetter("https://t.me/s/ssrlists", 200).Get()...)
+	node = append(node, getter.NewTGChannelGetter("https://t.me/s/ssrList", 200).Get()...)
+	node = append(node, getter.NewTGChannelGetter("https://t.me/s/SSRSUB", 200).Get()...)
+	node = append(node, getter.NewTGChannelGetter("https://t.me/s/FreeSSRNode", 200).Get()...)
+	node = append(node, getter.NewTGChannelGetter("https://t.me/s/ssrlists", 200).Get()...)
+	node = append(node, getter.NewTGChannelGetter("https://t.me/s/ssrshares", 200).Get()...)
+	node = append(node, getter.NewTGChannelGetter("https://t.me/s/V2List", 200).Get()...)
 
 	node = append(node, GetProxies()...)
 	node = proxy.Deduplication(node)
-	fmt.Println(len(node))
 	SetProxies(node)
 }

+ 10 - 3
getter/tgchannel.go

@@ -2,6 +2,7 @@ package getter
 
 import (
 	"fmt"
+	"strings"
 
 	"github.com/gocolly/colly"
 	"github.com/zu1k/proxypool/proxy"
@@ -14,7 +15,7 @@ type TGChannelGetter struct {
 	Url       string
 }
 
-func NewTGSsrlistGetter(url string, numNeeded int) *TGChannelGetter {
+func NewTGChannelGetter(url string, numNeeded int) *TGChannelGetter {
 	if numNeeded <= 0 {
 		numNeeded = 200
 	}
@@ -30,6 +31,7 @@ func (g TGChannelGetter) Get() []proxy.Proxy {
 	// 找到所有的文字消息
 	g.c.OnHTML("div.tgme_widget_message_text", func(e *colly.HTMLElement) {
 		g.Results = append(g.Results, proxy.GrepSSRLinkFromString(e.Text)...)
+		g.Results = append(g.Results, proxy.GrepVmessLinkFromString(e.Text)...)
 	})
 
 	// 找到之前消息页面的链接,加入访问队列
@@ -46,12 +48,17 @@ func (g TGChannelGetter) Get() []proxy.Proxy {
 	}
 
 	results := make([]proxy.Proxy, 0)
+	var data proxy.Proxy
 	for _, link := range g.Results {
-		data, err := proxy.ParseSSRLink(link)
+		if strings.HasPrefix(link, "ssr://") {
+			data, err = proxy.ParseSSRLink(link)
+		} else if strings.HasPrefix(link, "vmess://") {
+			data, err = proxy.ParseVmessLink(link)
+		}
 		if err != nil {
 			continue
 		}
-		results = append(results, *data)
+		results = append(results, data)
 	}
 	return results
 }

+ 17 - 11
provider/clash.go

@@ -11,11 +11,6 @@ type Clash struct {
 }
 
 func (c Clash) Provide() string {
-	//data, err := yaml.Marshal(c)
-	//if err != nil {
-	//
-	//}
-	//return string(data)
 	var resultBuilder strings.Builder
 
 	resultBuilder.WriteString("proxies:\n")
@@ -32,14 +27,18 @@ func checkClashSupport(p proxy.Proxy) bool {
 	switch p.(type) {
 	case proxy.ShadowsocksR:
 		ssr := p.(proxy.ShadowsocksR)
-		if checkInList(cipherList, ssr.Cipher) && checkInList(protocolList, ssr.Protocol) && checkInList(obfsList, ssr.Obfs) {
+		if checkInList(ssrCipherList, ssr.Cipher) && checkInList(ssrProtocolList, ssr.Protocol) && checkInList(ssrObfsList, ssr.Obfs) {
+			return true
+		}
+	case proxy.Vmess:
+		vmess := p.(proxy.Vmess)
+		if checkInList(vmessCipherList, vmess.Cipher) {
 			return true
-		} else {
-			return false
 		}
 	default:
 		return false
 	}
+	return false
 }
 
 func checkInList(list []string, item string) bool {
@@ -51,7 +50,7 @@ func checkInList(list []string, item string) bool {
 	return false
 }
 
-var cipherList = []string{
+var ssrCipherList = []string{
 	"aes-128-cfb",
 	"aes-192-cfb",
 	"aes-256-cfb",
@@ -75,7 +74,7 @@ var cipherList = []string{
 	"seed-cfb",
 }
 
-var obfsList = []string{
+var ssrObfsList = []string{
 	"plain",
 	"http_simple",
 	"http_post",
@@ -84,7 +83,7 @@ var obfsList = []string{
 	"tls1.2_ticket_fastauth",
 }
 
-var protocolList = []string{
+var ssrProtocolList = []string{
 	"origin",
 	"verify_deflate",
 	"verify_sha1",
@@ -96,3 +95,10 @@ var protocolList = []string{
 	"auth_chain_a",
 	"auth_chain_b",
 }
+
+var vmessCipherList = []string{
+	"auto",
+	"aes-128-gcm",
+	"chacha20-poly1305",
+	"none",
+}

+ 0 - 2
proxy/base.go

@@ -1,7 +1,5 @@
 package proxy
 
-import "C"
-
 type Base struct {
 	Name   string `yaml:"name" json:"name"`
 	Server string `yaml:"server" json:"server"`

+ 12 - 16
proxy/shadowsocksr.go

@@ -33,10 +33,6 @@ type ShadowsocksR struct {
 	Group         string `yaml:"group,omitempty" json:"group,omitempty"`
 }
 
-func (ssr *ShadowsocksR) ParseFromLink(ssrlink string) {
-	ssr, _ = ParseSSRLink(ssrlink)
-}
-
 func (ssr ShadowsocksR) Identifier() string {
 	return net.JoinHostPort(ssr.Server, strconv.Itoa(ssr.Port)) + ssr.ProtocolParam
 }
@@ -57,28 +53,28 @@ func (ssr ShadowsocksR) ToClash() string {
 	return "- " + string(data)
 }
 
-func ParseSSRLink(ssrlink string) (*ShadowsocksR, error) {
-	if !strings.HasPrefix(ssrlink, "ssr") {
-		return nil, ErrorNotSSRLink
+func ParseSSRLink(link string) (ShadowsocksR, error) {
+	if !strings.HasPrefix(link, "ssr") {
+		return ShadowsocksR{}, ErrorNotSSRLink
 	}
 
-	ssrmix := strings.SplitN(ssrlink, "://", 2)
+	ssrmix := strings.SplitN(link, "://", 2)
 	if len(ssrmix) < 2 {
-		return nil, ErrorNotSSRLink
+		return ShadowsocksR{}, ErrorNotSSRLink
 	}
 	linkPayloadBase64 := ssrmix[1]
 	payload, err := tool.Base64DecodeString(linkPayloadBase64)
 	if err != nil {
-		return nil, ErrorMissingQuery
+		return ShadowsocksR{}, ErrorMissingQuery
 	}
 
 	infoPayload := strings.SplitN(payload, "/?", 2)
 	if len(infoPayload) < 2 {
-		return nil, ErrorNotSSRLink
+		return ShadowsocksR{}, ErrorNotSSRLink
 	}
 	ssrpath := strings.Split(infoPayload[0], ":")
 	if len(ssrpath) < 6 {
-		return nil, ErrorPathNotComplete
+		return ShadowsocksR{}, ErrorPathNotComplete
 	}
 	// base info
 	server := strings.ToLower(ssrpath[0])
@@ -88,7 +84,7 @@ func ParseSSRLink(ssrlink string) (*ShadowsocksR, error) {
 	obfs := strings.ToLower(ssrpath[4])
 	password, err := tool.Base64DecodeString(ssrpath[5])
 	if err != nil {
-		return nil, ErrorPasswordParseFail
+		return ShadowsocksR{}, ErrorPasswordParseFail
 	}
 
 	moreInfo, _ := url.ParseQuery(infoPayload[1])
@@ -110,7 +106,7 @@ func ParseSSRLink(ssrlink string) (*ShadowsocksR, error) {
 	// protocol param
 	protocolParam, err := tool.Base64DecodeString(moreInfo.Get("protoparam"))
 	if err != nil {
-		return nil, ErrorProtocolParamParseFail
+		return ShadowsocksR{}, ErrorProtocolParamParseFail
 	}
 	if strings.HasSuffix(protocolParam, "_compatible") {
 		protocolParam = strings.ReplaceAll(protocolParam, "_compatible", "")
@@ -119,7 +115,7 @@ func ParseSSRLink(ssrlink string) (*ShadowsocksR, error) {
 	// obfs param
 	obfsParam, err := tool.Base64DecodeString(moreInfo.Get("obfsparam"))
 	if err != nil {
-		return nil, ErrorObfsParamParseFail
+		return ShadowsocksR{}, ErrorObfsParamParseFail
 	}
 	if strings.HasSuffix(obfsParam, "_compatible") {
 		obfsParam = strings.ReplaceAll(obfsParam, "_compatible", "")
@@ -131,7 +127,7 @@ func ParseSSRLink(ssrlink string) (*ShadowsocksR, error) {
 	//}
 	group := ""
 
-	return &ShadowsocksR{
+	return ShadowsocksR{
 		Base: Base{
 			Name:   remarks + "_" + strconv.Itoa(rand.Int()),
 			Server: server,

+ 218 - 0
proxy/vmess.go

@@ -0,0 +1,218 @@
+package proxy
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"math/rand"
+	"net"
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"github.com/zu1k/proxypool/tool"
+)
+
+var (
+	ErrorNotVmessLink          = errors.New("not a correct vmess link")
+	ErrorVmessPayloadParseFail = errors.New("vmess link payload parse failed")
+	//ErrorPasswordParseFail      = errors.New("password parse failed")
+	//ErrorPathNotComplete        = errors.New("path not complete")
+	//ErrorMissingQuery           = errors.New("link missing query")
+	//ErrorProtocolParamParseFail = errors.New("protocol param parse failed")
+	//ErrorObfsParamParseFail     = errors.New("obfs param parse failed")
+)
+
+type Vmess struct {
+	Base
+	UUID           string            `yaml:"uuid" json:"uuid"`
+	AlterID        int               `yaml:"alterId" json:"alterId"`
+	Cipher         string            `yaml:"cipher" json:"cipher"`
+	TLS            bool              `yaml:"tls,omitempty" json:"tls,omitempty"`
+	Network        string            `yaml:"network,omitempty" json:"network,omitempty"`
+	HTTPOpts       HTTPOptions       `yaml:"http-opts,omitempty" json:"http-opts,omitempty"`
+	WSPath         string            `yaml:"ws-path,omitempty" json:"ws-path,omitempty"`
+	WSHeaders      map[string]string `yaml:"ws-headers,omitempty" json:"ws-headers,omitempty"`
+	SkipCertVerify bool              `yaml:"skip-cert-verify,omitempty" json:"skip-cert-verify,omitempty"`
+	ServerName     string            `yaml:"servername,omitempty" json:"servername,omitempty"`
+}
+
+type HTTPOptions struct {
+	Method  string              `yaml:"method,omitempty" json:"method,omitempty"`
+	Path    []string            `yaml:"path,omitempty" json:"path,omitempty"`
+	Headers map[string][]string `yaml:"headers,omitempty" json:"headers,omitempty"`
+}
+
+func (v Vmess) Identifier() string {
+	return net.JoinHostPort(v.Server, strconv.Itoa(v.Port)) + v.Cipher
+}
+
+func (v Vmess) String() string {
+	data, err := json.Marshal(v)
+	if err != nil {
+		return ""
+	}
+	return string(data)
+}
+
+func (v Vmess) ToClash() string {
+	data, err := json.Marshal(v)
+	if err != nil {
+		return ""
+	}
+	return "- " + string(data)
+}
+
+type vmessLinkJson struct {
+	Add  string      `json:"add"`
+	V    string      `json:"v"`
+	Ps   string      `json:"ps"`
+	Port interface{} `json:"port"`
+	Id   string      `json:"id"`
+	Aid  string      `json:"aid"`
+	Net  string      `json:"net"`
+	Type string      `json:"type"`
+	Host string      `json:"host"`
+	Path string      `json:"path"`
+	Tls  string      `json:"tls"`
+}
+
+func ParseVmessLink(link string) (Vmess, error) {
+	if !strings.HasPrefix(link, "vmess") {
+		return Vmess{}, ErrorNotVmessLink
+	}
+
+	vmessmix := strings.SplitN(link, "://", 2)
+	if len(vmessmix) < 2 {
+		return Vmess{}, ErrorNotVmessLink
+	}
+	linkPayload := vmessmix[1]
+	if strings.Contains(linkPayload, "?") {
+		// 使用第二种解析方法
+		var infoPayloads []string
+		if strings.Contains(linkPayload, "/?") {
+			infoPayloads = strings.SplitN(linkPayload, "/?", 2)
+		} else {
+			infoPayloads = strings.SplitN(linkPayload, "?", 2)
+		}
+		if len(infoPayloads) < 2 {
+			return Vmess{}, ErrorNotVmessLink
+		}
+
+		baseInfo, err := tool.Base64DecodeString(infoPayloads[0])
+		if err != nil {
+			return Vmess{}, ErrorVmessPayloadParseFail
+		}
+		fmt.Println(baseInfo)
+		baseInfoPath := strings.Split(baseInfo, ":")
+		if len(baseInfoPath) < 3 {
+			return Vmess{}, ErrorPathNotComplete
+		}
+		// base info
+		cipher := baseInfoPath[0]
+		mixInfo := strings.SplitN(baseInfoPath[1], "@", 2)
+		if len(mixInfo) < 2 {
+			return Vmess{}, ErrorVmessPayloadParseFail
+		}
+		uuid := mixInfo[0]
+		server := mixInfo[1]
+		portStr := baseInfoPath[2]
+		port, err := strconv.Atoi(portStr)
+		if err != nil {
+			return Vmess{}, ErrorVmessPayloadParseFail
+		}
+
+		moreInfo, _ := url.ParseQuery(infoPayloads[1])
+		fmt.Println(moreInfo)
+		remarks := moreInfo.Get("remarks")
+		obfs := moreInfo.Get("obfs")
+		network := "tcp"
+		if obfs == "websocket" {
+			network = "ws"
+		}
+		//obfsParam := moreInfo.Get("obfsParam")
+		path := moreInfo.Get("path")
+		tls := moreInfo.Get("tls") == "1"
+
+		wsHeaders := make(map[string]string)
+		return Vmess{
+			Base: Base{
+				Name:   remarks + "_" + strconv.Itoa(rand.Int()),
+				Server: server,
+				Port:   port,
+				Type:   "vmess",
+				UDP:    false,
+			},
+			UUID:           uuid,
+			AlterID:        0,
+			Cipher:         cipher,
+			TLS:            tls,
+			Network:        network,
+			HTTPOpts:       HTTPOptions{},
+			WSPath:         path,
+			WSHeaders:      wsHeaders,
+			SkipCertVerify: true,
+			ServerName:     server,
+		}, nil
+	} else {
+		payload, err := tool.Base64DecodeString(linkPayload)
+		if err != nil {
+			return Vmess{}, ErrorVmessPayloadParseFail
+		}
+		vmessJson := vmessLinkJson{}
+		err = json.Unmarshal([]byte(payload), &vmessJson)
+		if err != nil {
+			return Vmess{}, err
+		}
+		port := 443
+		portInterface := vmessJson.Port
+		if i, ok := portInterface.(int); ok {
+			port = i
+		} else if s, ok := portInterface.(string); ok {
+			port, _ = strconv.Atoi(s)
+		}
+
+		alterId, err := strconv.Atoi(vmessJson.Aid)
+		if err != nil {
+			alterId = 0
+		}
+		tls := vmessJson.Tls == "tls"
+
+		wsHeaders := make(map[string]string)
+		wsHeaders["HOST"] = vmessJson.Host
+
+		return Vmess{
+			Base: Base{
+				Name:   vmessJson.Ps + "_" + strconv.Itoa(rand.Int()),
+				Server: vmessJson.Add,
+				Port:   port,
+				Type:   "vmess",
+				UDP:    false,
+			},
+			UUID:           vmessJson.Id,
+			AlterID:        alterId,
+			Cipher:         "auto",
+			TLS:            tls,
+			Network:        vmessJson.Net,
+			HTTPOpts:       HTTPOptions{},
+			WSPath:         vmessJson.Path,
+			WSHeaders:      wsHeaders,
+			SkipCertVerify: true,
+			ServerName:     vmessJson.Host,
+		}, nil
+	}
+}
+
+var (
+	vmessPlainRe = regexp.MustCompile("vmess://([A-Za-z0-9+/_-])+")
+)
+
+func GrepVmessLinkFromString(text string) []string {
+	results := make([]string, 0)
+	texts := strings.Split(text, "vmess://")
+	for _, text := range texts {
+		results = append(results, vmessPlainRe.FindAllString("vmess://"+text, -1)...)
+	}
+	return results
+}

+ 13 - 0
proxy/vmess_test.go

@@ -0,0 +1,13 @@
+package proxy
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestParseVmessLink(t *testing.T) {
+	//link := "vmess://YXV0bzo5MGQ0YTM3NC1kYWU4LTExZWEtODY3Zi01NjAwMDJlY2MzNDlAZml2ZWRlbWFuZHMubWw6NDQz?remarks=%E6%97%A5%E6%9C%AC5%EF%BC%9A%E7%94%B5%E6%8A%A5%E9%A2%91%E9%81%93%EF%BC%9A"
+
+	link := "vmess://ew0KICAidiI6ICIyIiwNCiAgInBzIjogIkBTU1JTVUItVjI5LeS7mOi0ueaOqOiNkDp0LmNuL0VHSkl5cmwiLA0KICAiYWRkIjogImJ1cmdlcmtpbmdnb29kLm1sIiwNCiAgInBvcnQiOiAiNDQzIiwNCiAgImlkIjogIjRiNWJhMzkwLWRhZjQtMTFlYS04ODEwLTU2MDAwMmVjYzk2NyIsDQogICJhaWQiOiAiNDYiLA0KICAibmV0IjogIndzIiwNCiAgInR5cGUiOiAibm9uZSIsDQogICJob3N0IjogImJ1cmdlcmtpbmdnb29kLm1sIiwNCiAgInBhdGgiOiAiL0M2bGt2UzNLLyIsDQogICJ0bHMiOiAidGxzIg0KfQ=="
+	fmt.Println(ParseVmessLink(link))
+}