diff --git a/.github/workflows/release_tag.yml b/.github/workflows/release_tag.yml index 2d22c03..261291d 100644 --- a/.github/workflows/release_tag.yml +++ b/.github/workflows/release_tag.yml @@ -78,7 +78,7 @@ jobs: - name: Build run: | mkdir -p build_assets - go build -v -o build_assets/v2raypool -trimpath -ldflags "-s -w -buildid= -X 'main.AppVersion=v1.6.3'" ./main + go build -v -o build_assets/v2raypool -trimpath -ldflags "-s -w -buildid= -X 'main.AppVersion=v1.6.4'" ./main - name: Rename Windows File if: matrix.goos == 'windows' diff --git a/CHANGELOG.md b/CHANGELOG.md index 92a1f6b..40df3d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 升级日志 +### v1.6.4 + +1. 新增删除节点功能 +2. 更新ss://节点格式解析规则 + ### v1.6.3 - 添加清除应用缓存功能 diff --git a/decode/shadowsocks.go b/decode/shadowsocks.go index 9e37214..1ee712d 100644 --- a/decode/shadowsocks.go +++ b/decode/shadowsocks.go @@ -15,52 +15,66 @@ type Shadowsocks struct { Port int } -// ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTozNlpDSGVhYlVTZktqZlFFdko0SERW@185.242.86.156:54170#%E4%BF%84%E7%BD%97%E6%96%AF+V2CROSS.COM -func ParseShadowsocks(u string) (ss Shadowsocks, err error) { - // var t *nurl.URL - // t, err = nurl.Parse(u) - // if err != nil { - // err = fmt.Errorf("invalid ss:// format") - // return - // } - // ss.Title = t.Fragment - // ss.Address = t.Hostname() - // ss.Port, err = strconv.Atoi(t.Port()) - // if err != nil { - // return - // } - // username := t.User.String() - // username, _ = Base64URLDecode(username) - // pwdinfos := strings.SplitN(username, ":", 2) - // if len(pwdinfos) != 2 { - // err = fmt.Errorf("parse password err") - // return - // } - // ss.Cipher = pwdinfos[0] - // ss.Password = pwdinfos[1] - - ninfo := strings.Split(u, "://") - if len(ninfo) != 2 { - err = fmt.Errorf("split :// err") +func ssrParse(u string) (ss Shadowsocks, uu *nurl.URL, err error) { + uu, err = nurl.Parse(u) + if err != nil { + err = fmt.Errorf("invalid ss:// format") return } - v := ninfo[1] - - pwdsplit := strings.Split(v, "@") - pwdinfo := pwdsplit[0] - var b1 string - b1, err = Base64StdDecode(pwdinfo) // base64.StdEncoding.DecodeString(pwdinfo) + ss.Title = uu.Fragment + ss.Address = uu.Hostname() + ss.Port, err = strconv.Atoi(uu.Port()) if err != nil { - err = fmt.Errorf("err(%v) for Base64StdDecode", err) return } - pwdsplit2 := strings.Split(b1, ":") - ss.Cipher = pwdsplit2[0] - ss.Password = pwdsplit2[1] - if strings.Contains(pwdsplit[1], "/?") { - addrsplit := strings.Split(pwdsplit[1], `/?`) - addrsplit2 := strings.Split(addrsplit[0], `:`) - argspre, _ := nurl.QueryUnescape(addrsplit[1]) + username := uu.User.String() + username, _ = Base64URLDecode(username) + pwdinfos := strings.SplitN(username, ":", 2) + if len(pwdinfos) != 2 { + err = fmt.Errorf("parse password err") + return + } + ss.Cipher = pwdinfos[0] + ss.Password = pwdinfos[1] + // ss.Plugin = uu.Query().Get("plugin") + ss.TransportStream.Path = uu.Query().Get("path") + ss.TransportStream.Protocol = uu.Query().Get("mode") + return +} + +// ParseShadowsocks. parse shadowsocks protocol url string. begin with: ss:// +func ParseShadowsocks(u string) (ss Shadowsocks, err error) { + var uu *nurl.URL + ss, uu, err = ssrParse(u) + if err != nil { + u = u[5:] + var l, ps string + if ind := strings.Index(u, "#"); ind > -1 { + l = u[:ind] + ps = u[ind+1:] + } else { + l = u + } + l, err = Base64StdDecode(l) + if err != nil { + l, err = Base64URLDecode(l) + if err != nil { + return + } + } + u = "ss://" + l + if ps != "" { + u += "#" + ps + } + ss, uu, err = ssrParse(u) + if err != nil { + return + } + } + + if uu.RawQuery != "" { + argspre, _ := nurl.QueryUnescape(uu.RawQuery) + args := strings.Split(argspre, `;`) for _, arg := range args { if strings.Contains(arg, "=") { @@ -68,28 +82,27 @@ func ParseShadowsocks(u string) (ss Shadowsocks, err error) { argval := argsplit[1] // plugin=v2ray-plugin if argsplit[0] == "mode" { - if argval == "websocket" { - argval = "ws" + if ss.TransportStream.Protocol == "" { + ss.TransportStream.Protocol = argval } - ss.TransportStream.Protocol = argval } if argsplit[0] == "path" { - ss.TransportStream.Path = argval + if ss.TransportStream.Path == "" { + ss.TransportStream.Path = argval + } } // if argsplit[0] == "mux"{ // // mux=true // } } - if strings.Contains(arg, "#") { - ss.Title = strings.Replace(arg, "#", "", 1) - } if arg == "tls" { ss.TransportStream.Security = arg - // nd.Tls = "tls" } } - ss.Address = addrsplit2[0] - ss.Port, err = strconv.Atoi(addrsplit2[1]) + } + + if ss.TransportStream.Protocol == "websocket" { + ss.TransportStream.Protocol = "ws" } return } diff --git a/decode/stream.go b/decode/stream.go index 2907b2f..e77413f 100644 --- a/decode/stream.go +++ b/decode/stream.go @@ -14,12 +14,12 @@ type StreamConfig struct { Protocol, Security, Path string } -func NewStreamConfig(proto string) *StreamConfig { - if proto == "ws" { - proto = "websocket" - } - return &StreamConfig{Protocol: proto} -} +// func NewStreamConfig(proto string) *StreamConfig { +// if proto == "ws" { +// proto = "websocket" +// } +// return &StreamConfig{Protocol: proto} +// } // { // "transport":"tcp", diff --git a/docs/coverpage.md b/docs/coverpage.md index 504be93..2e8d84e 100644 --- a/docs/coverpage.md +++ b/docs/coverpage.md @@ -1,6 +1,6 @@ -# V2rayPool v1.6.3 +# V2rayPool v1.6.4 > 简单易用的v2ray客户端和代理池服务 diff --git a/parse_nodes_test.go b/parse_nodes_test.go index 95b598c..34a3fc5 100644 --- a/parse_nodes_test.go +++ b/parse_nodes_test.go @@ -9,9 +9,22 @@ import ( "github.com/iotames/v2raypool/decode" ) -// func TestInitSubscribeData(t *testing.T) {} -type TestSubscribeData1 struct { - Protocol, Data string +func TestParseShadowsocks(t *testing.T) { + rawnds := []string{ + `ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTozNlpDSGVhYlVTZktqZlFFdko0SERW@ru1.abcd.com:1234/?plugin=v2ray-plugin%3bmode%3dwebsocket%3bpath%3d%2f%3bmux%3dtrue%3b#abcd-RU-Ru1`, + `ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTozNlpDSGVhYlVTZktqZlFFdko0SERW@usus.cdn.lifhgsgadjsad.xyz:47545#%F0%9F%87%BA%F0%9F%87%B8%20United%20States01`, + `ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTozNlpDSGVhYlVTZktqZlFFdko0SERW@185.242.86.156:54170#%E4%BF%84%E7%BD%97%E6%96%AF+V2CROSS.COM`, + } + rawdata := base64.StdEncoding.EncodeToString([]byte(strings.Join(rawnds, "\n"))) + dt, err := decode.ParseSubscribeByRaw(rawdata) + if err != nil { + t.Error(err) + } + nds := ParseV2rayNodes(dt) + if len(nds) != len(rawnds) { + t.Error("ParseV2rayNodes err") + } + // t.Logf("---TestParseShadowsocks---Result(%+v)---", nds) } // trojan://telegram-id-directvpn@18.224.236.198:22222?security=tls&&&#%E7%BE%8E%E5%9B%BD+Amazon%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83 diff --git a/proxy_node.go b/proxy_node.go index 5a248ac..4c115de 100644 --- a/proxy_node.go +++ b/proxy_node.go @@ -88,9 +88,6 @@ func (p ProxyNode) IsOk() bool { return time.Since(p.TestAt) < time.Hour*24 } -// // {"add":"jp6.xxx.top","host":"","id":"0999AE93-1330-4A75-DBC1-0DD545F7DD60","net":"ws","path":"","port":"41444","ps":"xxx-v2-JP-Tokyo6(1)","tls":"","v":2,"aid":0,"type":"none"} -// protocol, add, port id, net - type ProxyNodes []ProxyNode func (s ProxyNodes) Len() int { return len(s) } diff --git a/v2ray_parse.go b/v2ray_parse.go index e5b3bef..c4a3d62 100644 --- a/v2ray_parse.go +++ b/v2ray_parse.go @@ -71,7 +71,7 @@ func parseNodeInfo(d string) (nd V2rayNode, err error) { nd.Tls = ss.TransportStream.Security nd.Path = ss.TransportStream.Path nd.Ps = strings.TrimSpace(ss.Title) - if nd.Id == "" || nd.Type == "" || ss.Port == 0 || nd.Add == "" || nd.Net == "" { + if nd.Id == "" || nd.Type == "" || ss.Port == 0 || nd.Add == "" { err = fmt.Errorf("---parse--shadowsocks--err--ss://--raw(%s)---nd(%+v)", ssdata, nd) return }