add vmess
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
13
app/task.go
13
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package proxy
|
||||
|
||||
import "C"
|
||||
|
||||
type Base struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Server string `yaml:"server" json:"server"`
|
||||
|
||||
@@ -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
proxy/vmess.go
Normal file
218
proxy/vmess.go
Normal file
@@ -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
proxy/vmess_test.go
Normal file
13
proxy/vmess_test.go
Normal file
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user