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 }