From 76679d443f90bf76aeb2a6ecc0fb50c1df25e205 Mon Sep 17 00:00:00 2001
From: wuhanqing <554553400@qq.com>
Date: Sat, 20 Apr 2024 17:51:18 +0800
Subject: [PATCH] UPDATE to support shadowsocks
---
CHANGELOG.md | 4 ++
docs/coverpage.md | 2 +-
v2ray_api.go | 109 ++++++++++++++++++++++++++++++++++++----------
v2ray_config.go | 12 +++--
v2ray_parse.go | 14 ++++--
v2raypool.go | 15 +++++--
6 files changed, 122 insertions(+), 34 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 547456f..c7c8ef3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
## 升级日志
+### v1.5.0
+
+1. 支持shadowsocks(ss://)协议
+2. 完善vmess节点解析规则
### v1.4.1
diff --git a/docs/coverpage.md b/docs/coverpage.md
index 6560964..14a2d50 100644
--- a/docs/coverpage.md
+++ b/docs/coverpage.md
@@ -1,6 +1,6 @@
-# V2rayPool v1.3.7
+# V2rayPool v1.5.0
> 简单易用的v2ray客户端和代理池服务
diff --git a/v2ray_api.go b/v2ray_api.go
index ffc7a4a..1a1acb6 100644
--- a/v2ray_api.go
+++ b/v2ray_api.go
@@ -18,15 +18,15 @@ import (
"github.com/v2fly/v2ray-core/v5/common/net"
// "github.com/v2fly/v2ray-core/v5/proxy/blackhole"
- // "github.com/v2fly/v2ray-core/v5/proxy/freedom"
+ "github.com/v2fly/v2ray-core/v5/proxy/freedom"
// "github.com/v2fly/v2ray-core/v5/proxy/dokodemo"
// "github.com/v2fly/v2ray-core/v5/proxy/socks"
// "github.com/v2fly/v2ray-core/v5/common/uuid"
"github.com/v2fly/v2ray-core/v5/proxy/http"
+ "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks"
"github.com/v2fly/v2ray-core/v5/proxy/socks"
"github.com/v2fly/v2ray-core/v5/proxy/vmess"
- // "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks"
// "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022"
vmessOutbound "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound"
"google.golang.org/grpc"
@@ -111,13 +111,20 @@ func (a V2rayApiClient) RemoveInbound(intag string) error {
return err
}
-func getTransportStreamConfig(transproto, path string) (conf *internet.StreamConfig, err error) {
+func getTransportStreamConfig(nd V2rayNode, hdhost string) (conf *internet.StreamConfig, err error) {
+ transproto := nd.Net
+ path := nd.Path
var transptl internet.TransportProtocol
var protoconf proto.Message
switch transproto {
case "ws", "websocket":
transptl = internet.TransportProtocol_WebSocket
- protoconf = &websocket.Config{Path: path}
+ wsconf := websocket.Config{Path: path}
+ if hdhost != "" {
+ // wsconf.UseBrowserForwarding = true
+ wsconf.Header = []*websocket.Header{{Key: "Host", Value: hdhost}}
+ }
+ protoconf = &wsconf
case "tcp":
transptl = internet.TransportProtocol_TCP
protoconf = &tcp.Config{}
@@ -145,23 +152,28 @@ func (a V2rayApiClient) AddOutboundByV2rayNode(nd V2rayNode, outag string) error
return fmt.Errorf("outbound protocol not support %s. only support vmess, ss, shadowsocks", nd.Protocol)
}
}
- streamConf, err := getTransportStreamConfig(nd.Net, nd.Path)
+ var streamConf *internet.StreamConfig
+ var resp *pros.AddOutboundResponse
+ var err error
+
+ streamConf, err = getTransportStreamConfig(nd, "")
if err != nil {
return err
}
- outsendset := proxyman.SenderConfig{
+ sender := proxyman.SenderConfig{
StreamSettings: streamConf,
}
if nd.Tls == "tls" {
- outsendset.StreamSettings.SecurityType = serial.GetMessageType(&tls.Config{})
- outsendset.StreamSettings.SecuritySettings = []*anypb.Any{
+ sender.StreamSettings.SecurityType = serial.GetMessageType(&tls.Config{})
+ sender.StreamSettings.SecuritySettings = []*anypb.Any{
serial.ToTypedMessage(&tls.Config{
AllowInsecure: true,
}),
}
}
- proxyport, err := nd.Port.Int64()
+ var proxyport int64
+ proxyport, err = nd.Port.Int64()
if err != nil {
return fmt.Errorf("err AddOutboundByV2rayNode 端口数据解析错误 port val(%v)--err(%v)", nd.Port, err)
}
@@ -189,21 +201,72 @@ func (a V2rayApiClient) AddOutboundByV2rayNode(nd V2rayNode, outag string) error
},
},
}
+ resp, err = a.c.AddOutbound(a.ctx, &pros.AddOutboundRequest{Outbound: &v5.OutboundHandlerConfig{
+ Tag: outag,
+ SenderSettings: serial.ToTypedMessage(&sender),
+ ProxySettings: serial.ToTypedMessage(proxySet),
+ }})
+
}
- // if nd.Protocol == "shadowsocks" || nd.Protocol == "ss" {
- // // proxySet = nil TODO
- // }
-
- resp, err := a.c.AddOutbound(a.ctx, &pros.AddOutboundRequest{Outbound: &v5.OutboundHandlerConfig{
- Tag: outag,
- SenderSettings: serial.ToTypedMessage(&outsendset),
- ProxySettings: serial.ToTypedMessage(proxySet),
- // ProxySettings: serial.ToTypedMessage(&freedom.Config{
- // DomainStrategy: freedom.Config_AS_IS,
- // UserLevel: 0,
- // }),
- }})
- fmt.Printf("---AddOutbound(%s)--(%s:%v)-portType(%T)--result(%s)--err(%v)--\n", outag, nd.Add, nd.Port, nd.Port, resp, err)
+ if nd.Protocol == "shadowsocks" || nd.Protocol == "ss" {
+ ssAccount := serial.ToTypedMessage(&shadowsocks.Account{
+ Password: nd.Id,
+ CipherType: func() shadowsocks.CipherType {
+ method := strings.ReplaceAll(nd.Type, "-", "_") // "aes-256-gcm",
+ method = strings.ToUpper(method)
+ val, ok := shadowsocks.CipherType_value[method]
+ if ok {
+ return shadowsocks.CipherType(val)
+ }
+ return shadowsocks.CipherType_AES_256_GCM
+ }(),
+ })
+ proxySet = &shadowsocks.ClientConfig{
+ Server: []*protocol.ServerEndpoint{
+ {
+ Address: net.NewIPOrDomain(net.DomainAddress(nd.Add)),
+ Port: uint32(proxyport),
+ User: []*protocol.User{{
+ Account: ssAccount,
+ }},
+ },
+ },
+ }
+ sender.ProxySettings = &internet.ProxyConfig{
+ Tag: outag + "-dialer",
+ }
+ resp, err = a.c.AddOutbound(a.ctx, &pros.AddOutboundRequest{Outbound: &v5.OutboundHandlerConfig{
+ Tag: outag,
+ SenderSettings: serial.ToTypedMessage(&sender),
+ ProxySettings: serial.ToTypedMessage(proxySet),
+ }})
+ fmt.Printf("---AddOutbound--shadowsocks(%s)--(%s:%v)--result(%s)--err(%v)--\n", outag, nd.Add, nd.Port, resp, err)
+ resp, err = a.c.AddOutbound(a.ctx, &pros.AddOutboundRequest{Outbound: &v5.OutboundHandlerConfig{
+ Tag: outag + "-dialer",
+ SenderSettings: serial.ToTypedMessage(func() *proxyman.SenderConfig {
+ streamConf, _ = getTransportStreamConfig(nd, "cloudflare.com")
+ sender := proxyman.SenderConfig{
+ StreamSettings: streamConf,
+ MultiplexSettings: &proxyman.MultiplexingConfig{
+ Enabled: true,
+ Concurrency: 1,
+ },
+ }
+ return &sender
+ }()),
+ ProxySettings: serial.ToTypedMessage(&freedom.Config{
+ DomainStrategy: freedom.Config_AS_IS,
+ DestinationOverride: &freedom.DestinationOverride{
+ Server: &protocol.ServerEndpoint{
+ Address: net.NewIPOrDomain(net.DomainAddress(nd.Add)),
+ Port: uint32(proxyport),
+ },
+ },
+ }),
+ }})
+ }
+
+ fmt.Printf("---AddOutbound(%s)--(%s:%v)-result(%s)--err(%v)--\n", outag, nd.Add, nd.Port, resp, err)
return err
}
diff --git a/v2ray_config.go b/v2ray_config.go
index 41f121e..1781f9f 100644
--- a/v2ray_config.go
+++ b/v2ray_config.go
@@ -39,7 +39,7 @@ type V2rayInbound struct {
type V2rayOutbound struct {
Protocol string `json:"protocol"`
- SendThrough string `json:"sendThrough"` // 用于发送数据的 IP 地址,当主机有多个 IP 地址时有效,默认值为 "0.0.0.0"。
+ SendThrough *string `json:"sendThrough"` // 用于发送数据的 IP 地址,当主机有多个 IP 地址时有效,默认值为 "0.0.0.0"。
Tag string `json:"tag"`
Settings json.RawMessage `json:"settings"` // 视协议不同而不同。详见每个协议中的 OutboundConfigurationObject
// "streamSettings":{"network":"%s","tlsSettings":{"disableSystemRoot":false},"wsSettings":{"path":""},"xtlsSettings":{"disableSystemRoot":false}}
@@ -187,7 +187,10 @@ func setV2rayConfigV4Routing(confv4 *V2rayConfigV4, cf conf.Conf, inPort int) {
// setV2rayConfigV4Outbounds 出站配置
// n.Add != "" 时,启用单节点代理模式。
func setV2rayConfigV4Outbounds(confv4 *V2rayConfigV4, n V2rayNode) error {
- outdirect := V2rayOutbound{Protocol: "freedom", Tag: TAG_OUTBOUND_DIRECT, SendThrough: "0.0.0.0"}
+ sendth := "0.0.0.0"
+ // sendth.Address = net.ParseAddress()
+ // fmt.Printf("-------setV2rayConfigV4Outbounds---SendThrough---ip(%+v)--domain(%+v)--str(%s)-\n", sendth.Address.IP(), sendth.Address.Domain(), sendth.Address)
+ outdirect := V2rayOutbound{Protocol: "freedom", Tag: TAG_OUTBOUND_DIRECT, SendThrough: &sendth}
outdirect.Settings = json.RawMessage(`{}`)
outdirect.StreamSetting = json.RawMessage(`{}`)
@@ -196,7 +199,7 @@ func setV2rayConfigV4Outbounds(confv4 *V2rayConfigV4, n V2rayNode) error {
networkset := n.Net
outbd1 := V2rayOutbound{
Protocol: n.Protocol,
- SendThrough: "0.0.0.0",
+ SendThrough: &sendth,
Tag: TAG_OUTBOUND_ACTIVE,
}
@@ -219,7 +222,8 @@ func setV2rayConfigV4Outbounds(confv4 *V2rayConfigV4, n V2rayNode) error {
return nil
}
if n.Protocol == "ss" || n.Protocol == "shadowsocks" {
- settingstr := fmt.Sprintf(`"servers":[{"address":"%s","method":"%s","password":"%s","port":%s}]`, n.Add, n.Type, n.Id, n.Port)
+ outbd1.Protocol = "shadowsocks"
+ settingstr := fmt.Sprintf(`{"servers":[{"address":"%s","method":"%s","password":"%s","port":%s}]}`, n.Add, n.Type, n.Id, n.Port)
outbd1.Settings = json.RawMessage(settingstr)
proxysetTag := fmt.Sprintf("%s-dialer", outbd1.Tag)
outbd1.ProxySettings = json.RawMessage(fmt.Sprintf(`{"tag":"%s"}`, proxysetTag))
diff --git a/v2ray_parse.go b/v2ray_parse.go
index 8550e1b..8652bc4 100644
--- a/v2ray_parse.go
+++ b/v2ray_parse.go
@@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
+ nurl "net/url"
"strings"
)
@@ -58,17 +59,19 @@ func parseNodeInfo(d string) (nd V2rayNode, err error) {
if nd.Protocol == "ss" {
pwdsplit := strings.Split(ninfo[1], "@")
pwdinfo := pwdsplit[0]
- b, err = base64.StdEncoding.DecodeString(pwdinfo)
+ var b1 string
+ b1, err = Base64StdDecode(pwdinfo) // base64.StdEncoding.DecodeString(pwdinfo)
if err != nil {
err = fmt.Errorf("parseNodeInfo err(%v) for ss:// base64 DecodeString", err)
return
}
- pwdsplit2 := strings.Split(string(b), ":")
+ pwdsplit2 := strings.Split(b1, ":")
method := pwdsplit2[0]
password := pwdsplit2[1]
addrsplit := strings.Split(pwdsplit[1], `/?`)
addrsplit2 := strings.Split(addrsplit[0], `:`)
- args := strings.Split(addrsplit[1], `;`)
+ argspre, _ := nurl.QueryUnescape(addrsplit[1])
+ args := strings.Split(argspre, `;`)
for _, arg := range args {
if strings.Contains(arg, "=") {
argsplit := strings.Split(arg, "=")
@@ -98,6 +101,11 @@ func parseNodeInfo(d string) (nd V2rayNode, err error) {
nd.Port = json.Number(addrsplit2[1])
nd.Type = method
nd.Id = password
+ portint, _ := nd.Port.Int64()
+ if nd.Id == "" || nd.Type == "" || portint == 0 || nd.Add == "" || nd.Net == "" {
+ err = fmt.Errorf("---parse--shadowsocks--err--ss://--raw(%s)---nd(%+v)", ninfo[1], nd)
+ return
+ }
return
}
if nd.Protocol == "trojan" {
diff --git a/v2raypool.go b/v2raypool.go
index 2dc2a1b..14ce51e 100644
--- a/v2raypool.go
+++ b/v2raypool.go
@@ -445,9 +445,13 @@ func (p *ProxyPool) TestAllForce() {
return
}
p.nodes.SortBySpeed()
-
if p.activeCmd == nil {
- p.ActiveNode(p.nodes[0], true)
+ for _, nd := range p.nodes {
+ if nd.IsOk() {
+ p.ActiveNode(nd, true)
+ break
+ }
+ }
}
p.IsLock = false
}
@@ -501,7 +505,12 @@ func (p *ProxyPool) TestAll() {
}
p.nodes.SortBySpeed()
if p.activeCmd == nil {
- p.ActiveNode(p.nodes[0], true)
+ for _, nd := range p.nodes {
+ if nd.IsOk() {
+ p.ActiveNode(nd, true)
+ break
+ }
+ }
}
p.IsLock = false
}