From 2e5a33feb174765d157ff981cb079e16df9d8c80 Mon Sep 17 00:00:00 2001 From: e1732a364fed <75717694+e1732a364fed@users.noreply.github.com> Date: Sat, 1 Jan 2000 00:00:00 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E8=AE=A2=E6=96=87=E6=A1=A3=EF=BC=8C?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=EF=BC=8C=E4=BB=A3=E7=A0=81;=E8=A7=A3?= =?UTF-8?q?=E5=86=B3http=E5=A4=B4=E5=B1=82=E5=9B=9E=E8=90=BD=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=AF=BC=E8=87=B4panic=E7=9A=84bug;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加 encrypt_algo 配置; 添加 configAdapter包 --- README.md | 62 +++++++++----------------------- configAdapter/package.go | 52 +++++++++++++++++++++++++++ examples/socks5.toml | 27 ++++++++------ examples/ss.client.toml | 12 +++++-- examples/ss.server.toml | 5 +-- examples/vmess.client.toml | 7 ++-- httpLayer/header.go | 14 ++++++-- iics.go | 6 ++++ netLayer/addr.go | 50 ++++++++++++++++++-------- netLayer/const.go | 6 ++-- proxy/base.go | 2 +- proxy/config_proxy.go | 8 +++-- proxy/direct.go | 8 ++--- proxy/http/server.go | 3 +- proxy/shadowsocks/shadowsocks.go | 27 ++------------ proxy/vmess/client.go | 46 +++++++++++++----------- utils/algo.go | 27 ++++++++++++++ utils/algo_test.go | 11 ++++++ utils/error.go | 8 ++++- utils/user.go | 31 +++------------- 20 files changed, 247 insertions(+), 165 deletions(-) create mode 100644 configAdapter/package.go diff --git a/README.md b/README.md index 46c7b38..2e7a5dc 100644 --- a/README.md +++ b/README.md @@ -30,17 +30,20 @@ verysimple 研发了一些新技术,使用自研架构,可以加速,目前 vs的一些亮点是 全协议readv加速,lazy技术,vless v1,hysteria 阻控,更广泛的utls支持,grpc回落,交互模式等。 -支持的功能有: +_喜大泪奔,本作的先行研究启发了xray项目,使其开发了 vision 流控。本作也算为代理界做出了一些贡献~_ -socks5(包括 udp associate 以及用户密码)/http(以及用户密码)/socks5http(与clash的mixed等价)/dokodemo/tproxy(透明代理)/trojan/simplesocks/vless(v0/v1)/vmess, 多用户, -ws(以及earlydata)/grpc(以及multiMode,uTls,以及 支持回落的 grpcSimple)/quic(以及hy阻控、手动挡 和 0-rtt)/smux, +## 支持的功能 + +socks5(包括 udp associate 以及用户密码)/http(以及用户密码)/socks5http(与clash的mixed等价)/dokodemo/tproxy(透明代理)/trojan/simplesocks/vless(v0/**v1**)/vmess/shadowsocks, 多用户, http头 + +ws(以及earlydata)/grpc(以及multiMode,uTls,以及 **支持回落的 grpcSimple**)/quic(以及**hy阻控、手动挡** 和 0-rtt)/smux, dns(udp/tls)/route(geoip/geosite,分流功能完全与v2ray等价)/fallback(path/sni/alpn/PROXY protocol v1/v2), sniffing(tls) -tcp/udp(以及fullcone)/unix domain socket, tls(包括客户端证书验证), uTls,【tls lazy encrypt】, http伪装头,PROXY protocol v1/v2 监听, +tcp/udp(以及fullcone)/unix domain socket, tls(包括客户端证书验证), uTls,**【tls lazy encrypt】**, http伪装头(**可支持回落**),PROXY protocol v1/v2 监听, -cli(交互模式)/apiServer, Docker, docker-compose. +cli(**交互模式**)/apiServer, Docker, docker-compose. 为了不吓跑小白,本 README 把安装、使用方式 放在了前面,如果你要直接阅读本作的技术介绍部分,点击跳转 -> [创新点](#创新点) @@ -49,17 +52,9 @@ cli(交互模式)/apiServer, Docker, docker-compose. ## 安装方式: -对觉得本作安装很复杂的人,我再强调一遍: - -本作对标的是 v2ray和xray等内核,不是对标的“安装脚本”,**本作是个内核**,再说一遍。内核能支持各种交互模式已经很强大了好不好。 - -你见哪个内核项目负责人自己上来就提供完整一键脚本的?都是其他人帮着提供的,我这么忙哪有时间研究一键脚本。有需求的你可以写一个然后提PR啊。 - -本内核完全是我自己写的,完全不同于 xray这种 fork的版本,所以我很忙的。 - ### 下载安装 -如果是 linux服务器,可以参考我的一篇指导文章 [install.md](docs/install.md) +如果是 linux服务器,可参考指导文章 [install.md](docs/install.md). 电脑客户端的话直接自己到release下载就行。 @@ -136,7 +131,7 @@ verysimple -c server.json 极简模式暂不支持 ws/grpc 特性. -极简模式继承自v2simple,理念是字越少越好。推荐没有极简需求的同学直接使用标准模式。 +极简模式继承自 v2simple, 理念是字越少越好。推荐没有极简需求的同学直接使用标准模式。 verysimple 继承 v2simple的一个优点,就是服务端的配置也可以用url做到。谁规定url只能用于分享客户端配置了?一条url肯定比json更容易配置,不容易出错。 @@ -300,7 +295,7 @@ v0协议是直接兼容现有v2ray/xray的,比如可以客户端用任何现 使用readv 进行加速 -其它监听协议还支持 socks5, http, dokodemo +其它监听协议还支持 socks5, http, dokodemo,vmess, simplesocks, shadowsocks 等 多种配置文件格式,包括自有的 toml标准格式 @@ -314,11 +309,11 @@ v0协议是直接兼容现有v2ray/xray的,比如可以客户端用任何现 支持grpc,与 xray/v2ray兼容; 还有 grpcSimple,见上文。 -真实 nginx响应。 +真实 nginx拒绝响应。 支持 quic以及hysteria 阻控,与xray/v2ray兼容(详情见wiki),还新开发了“手动挡”模式 -api服务器;tproxy 透明代理; http伪装头. +api服务器;tproxy 透明代理; http头(即所谓的混淆、伪装头等), 该模式下还支持回落。 本作支持 trojan-go 的 “可插拔模块”模式的。而且也可以用build tag 来开启或关闭某项功能。不过本作为了速度,耦合高一些。 @@ -504,9 +499,9 @@ MIT协议,即你用的时候也要附带一个MIT文件,然后作者不承 启发自我fork的v2simple,不过原作者的架构还是有点欠缺,我就直接完全重构了,完全使用我自己的代码。 -## 额外说明 以及 开发计划 +## 开发计划 -计划有 +远期计划有 1. 完善并实现 vless v1协议 2. 什么时候搞一个 verysimple_c 项目,用c语言照着写一遍; 也就是说,就算本verysimple没有任何技术创新,单单架构简单也是有技术优势的,可以作为参考 实现更底层的 c语言实现。之后本以为可以加入naiveproxy,但是实际发现没那么简单. @@ -597,37 +592,14 @@ verysimple 版本 v1.0.3 测速时,打开的窗口尽量少,且只留浏览器的窗口在最前方。已经证明多余的窗口会影响速率。尤其是这种消耗cpu性能的情况,在核显的电脑上确实要保证cpu其它压力减到最小。 -## 交流 +## 交流与思想 群肯定是有的。只在此山中,云深不知处。实际上每一个群都有可能是verysimple群,每一个成员都有可能是verysimple的作者。 -如果你实在找不到群,你不妨自己建一个,然后自称verysimple项目作者。 - -建议所有的人都认真阅读README以及其它所有有文字的文件和页面; - -有能力的人要阅读整个verysimple项目的所有代码; - -希望每一个人都能站出来,自豪地说,“我就是原作者”,并且能够滔滔不绝地讲解自己对verysimple的架构的理解。 - -如果你能fork,并青出于蓝,那么我甘拜下风。 - -也希望本项目能够普及到世界上所有需要学习相关技术的国家,希望所有的想要学习代码的人都能够先学习中文。 +如果你实在找不到群,你不妨自己建一个。希望每一个人都能站出来,自豪地说,“我就是原作者”,并且能够滔滔不绝地讲解自己对verysimple的架构的理解。关键不在于谁是作者,一个作者倒下,千万个作者会站起来。 如果本作作者突然停更,这里允许任何人以 verysimple 作者的名义fork并 接盘。你只要声称自己是原作者,忘记了github和自己邮箱的密码,只好重开,这不就ok了。 -关键不在于谁是作者,一个作者倒下,千万个作者会站起来。 - -我们的思想 生生不息,追求自由的人们啊,一起奋斗吧! - ->鱼,我所欲也;熊掌,亦我所欲也。二者不可得兼,舍鱼而取熊掌者也。生,亦我所欲也;义,亦我所欲也。二者不可得兼,舍生而取义者也。 - ->砍头不要紧, -只要主义真。 -杀了夏明翰, -还有后来人。 - - - # 免责声明与鸣谢 diff --git a/configAdapter/package.go b/configAdapter/package.go new file mode 100644 index 0000000..4b33898 --- /dev/null +++ b/configAdapter/package.go @@ -0,0 +1,52 @@ +/* +package configAdapter provides methods to convert proxy.ListenConf and proxy.DialConf to some 3rd party formats. It also defines some extra config formats used in vs. + +计划支持 quantumultX, clash, 以及 v2rayN 的配置格式 + +参考 https://github.com/e1732a364fed/v2ray_simple/discussions/163 +*/ +package configAdapter + +import ( + "strings" + + "github.com/e1732a364fed/v2ray_simple/proxy" +) + +/* +quantumult X 只支持 vmess,trojan,shadowsocks,http 这四种协议. +See https://github.com/crossutility/Quantumult-X/blob/master/sample.conf +*/ +func ToQX(dc *proxy.DialConf) string { + var sb strings.Builder + + sb.WriteString(dc.Protocol) + sb.WriteByte('=') + sb.WriteString(dc.GetAddrStr()) + sb.WriteString(", ") + + switch dc.Protocol { + case "vmess": + sb.WriteString("method=") + ea := dc.EncryptAlgo + if ea == "" { + ea = "none" + } + sb.WriteString(ea) + sb.WriteString(", ") + + } + sb.WriteString("password=") + sb.WriteString(dc.Uuid) + + return sb.String() +} + +func ToClash(dc *proxy.DialConf) string { + + return "" +} + +func ToV2rayN(dc *proxy.DialConf) string { + return "" +} diff --git a/examples/socks5.toml b/examples/socks5.toml index f25d971..29cb6c0 100644 --- a/examples/socks5.toml +++ b/examples/socks5.toml @@ -1,21 +1,25 @@ -# 本文件 可 测试socks 的 dial, 以及密码认证 等功能 是否可用;socks5进,socks5出,然后又socks5进,direct出 +# 本文件 可 测试socks 的 dial, 以及密码认证 等功能 是否可用; +# 本文件所定义的传导链是: ->socks5->socks5->direct # 另外你可以看到,listen、dial和 route的顺序是可以打乱的, # 这样 自己可以方便地 把相关联 的配置 放在一起。 +# vs暂未给出http代理的示例配置,因为完全和socks5类似,只需要把 protocol 改为 http即可 + [[listen]] tag = "my_socks5_1" protocol = "socks5" -host = "127.0.0.1" -port = 10800 +host = "127.0.0.1" +port = 10800 [[dial]] -protocol = "socks5" +protocol = "socks5" tag = "dial1" -host = "127.0.0.1" +host = "127.0.0.1" port = 10801 -#uuid = "user:very\npass:simple" +#uuid = "user:very\npass:simple" #vs 的逻辑是,uuid相当于一个登陆token, 能完整确认一个用户的身份以及登陆权限, 用户名和密码加起来提供了足够的信息,足以用做token, 所以都填到uuid里. + uuid = "user:admin\npass:nimda" [[route]] @@ -23,8 +27,8 @@ fromTag = ["my_socks5_1"] toTag = "dial1" [[listen]] -tag = "my_socks5_2" -protocol = "socks5" +tag = "my_socks5_2" +protocol = "socks5" host = "127.0.0.1" port = 10801 @@ -40,7 +44,10 @@ pass:nimda # 或者这种,但是看起来不太美观 #uuid = "user:very\npass:simple" -users = [ {user = "very", pass = "simple"}, {user = "v2ray", pass = "hard"} ] #也可以用users 来存储多个用户 +users = [ + { user = "very", pass = "simple" }, + { user = "v2ray", pass = "hard" }, +] #也可以用users 来存储多个用户 [[dial]] tag = "direct" @@ -48,4 +55,4 @@ protocol = "direct" [[route]] fromTag = ["my_socks5_2"] -toTag = "direct" \ No newline at end of file +toTag = "direct" diff --git a/examples/ss.client.toml b/examples/ss.client.toml index b9c4c94..0d5891a 100644 --- a/examples/ss.client.toml +++ b/examples/ss.client.toml @@ -6,7 +6,15 @@ port = 10800 [[dial]] protocol = "shadowsocks" -uuid = "method:AES-128-GCM\npass:iloveverysimple" # chacha20-ietf-poly1305,AES-128-GCM, AES-256-GCM, 大小写均可;在macm1 上测试 应该是 aes 更快 + host = "127.0.0.1" port = 4434 -#network = "mix" \ No newline at end of file + +uuid = "method:AES-128-GCM\npass:iloveverysimple" + +# 之所以uuid这么写,完全是shadowsocks的规范所定义的,它要求 服务端和客户端所指定的 method必须匹配; +# 从逻辑上讲,这个method也加入了鉴权。类似的配置还可以参考 socks5.toml + +#encrypt_algo = "AES-128-GCM" # chacha20-ietf-poly1305,AES-128-GCM, AES-256-GCM, 大小写均可;一般应该是 aes 更快, 如果 encrypt_algo 给出,会覆盖 uuid中的 method 部分 + +#network = "dual" #ss同时使用tcp和udp分别传输tcp和udp数据,属于双栈 \ No newline at end of file diff --git a/examples/ss.server.toml b/examples/ss.server.toml index 88a6e14..5a55e13 100644 --- a/examples/ss.server.toml +++ b/examples/ss.server.toml @@ -1,6 +1,7 @@ [[listen]] protocol = "shadowsocks" -uuid = "method:AES-128-GCM\npass:iloveverysimple" host = "127.0.0.1" port = 4434 -#network = "mix" \ No newline at end of file +uuid = "method:AES-128-GCM\npass:iloveverysimple" + +#network = "dual" \ No newline at end of file diff --git a/examples/vmess.client.toml b/examples/vmess.client.toml index 8df9820..e6547c8 100644 --- a/examples/vmess.client.toml +++ b/examples/vmess.client.toml @@ -9,10 +9,11 @@ protocol = "vmess" uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" host = "127.0.0.1" port = 4434 -extra = { vmess_security = "aes-128-gcm" } # 其他可能的值: "chacha20-poly1305", "auto", "none", "", -# ( "" 这种 空字符串 就是对应的 v2ray文档中的 zero , 所以这个 vmess_security 配置 你要是不给出, 那么程序就会默认使用 zero) +extra = { vmess_security = "aes-128-gcm" } # 其他可能的值: "chacha20-poly1305", "auto", "none", "" -# 你也可以写zero, 效果一样,但是谁会多写四个字符呢,本来空字符串就是 空 的含义啊! 总之我们尽量求简 +encrypt_algo = "aes-128-gcm" #最新vs代码 添加了新的 encrypt_algo配置项, 同样可以配置 vmess 的 security; 它和 extra.vmess_security 同时出现时,encrypt_algo 会覆盖 extra.vmess_security + +# 这里vs为了保护用户,如果不给出内部加密方式,会自动设为auto,而不是zero # 具体none和zero的区别, 以及 auto 的含义, 详见 https://www.v2fly.org/config/protocols/vmess.html#userobject diff --git a/httpLayer/header.go b/httpLayer/header.go index 149987c..72b406c 100644 --- a/httpLayer/header.go +++ b/httpLayer/header.go @@ -367,10 +367,18 @@ func (c *HeaderConn) Read(p []byte) (n int, err error) { rp, buf, err = c.H.ReadRequest(c.Conn) if err != nil { - err = &utils.ErrBuffer{ - Err: utils.ErrInErr{ErrDesc: "http HeaderConn Read failed, at serverEnd", ErrDetail: err}, - Buf: rp.WholeRequestBuf, + const errDesc = "http HeaderConn Read failed, at serverEnd" + + if rp.WholeRequestBuf != nil { + + err = &utils.ErrBuffer{ + Err: utils.ErrInErr{ErrDesc: errDesc, ErrDetail: err}, + Buf: rp.WholeRequestBuf, + } + } else { + err = &utils.ErrInErr{ErrDesc: errDesc, ErrDetail: err} } + return } if c.ReadOkCallback != nil { diff --git a/iics.go b/iics.go index 5dd60a2..497c250 100644 --- a/iics.go +++ b/iics.go @@ -144,7 +144,13 @@ func (iics *incomingInserverConnState) extractFirstBufFromErr(err error) bool { if firstbuffer := be.Buf; firstbuffer == nil { //不应该,至少能读到1字节的。 + if ce := utils.CanLogErr("No FirstBuffer"); ce != nil { + ce.Write( + zap.Any("params", be.Buf), + ) + + } panic("No FirstBuffer") } else { diff --git a/netLayer/addr.go b/netLayer/addr.go index 2af0ac6..420b2fc 100644 --- a/netLayer/addr.go +++ b/netLayer/addr.go @@ -641,17 +641,17 @@ func V2rayGetAddrFrom(buf utils.ByteReader) (addr Addr, err error) { return } -const MixNetworkName = "tcp/udp" +const DualNetworkName = "tcp/udp" -type TCP_or_UDPAddr struct { +type TCPUDPAddr struct { *net.TCPAddr *net.UDPAddr } -func (tu *TCP_or_UDPAddr) Network() string { - return MixNetworkName +func (tu *TCPUDPAddr) Network() string { + return DualNetworkName } -func (tu *TCP_or_UDPAddr) String() string { +func (tu *TCPUDPAddr) String() string { return tu.TCPAddr.String() + " / " + tu.UDPAddr.String() } @@ -675,18 +675,24 @@ func StrToNetAddr(network, s string) (net.Addr, error) { } return net.ResolveUDPAddr(network, s) - case Mix: - ta, e := StrToNetAddr("tcp", s) - if e != nil { - return nil, e - } + case Dual: + //两种配置方式,一种是给出一个地址,tcp和udp均使用该地址; + // 另一种是 分别指出 两种协议的 地址. - ua, e := StrToNetAddr("udp", s) - if e != nil { - return nil, e - } + if strings.HasPrefix(s, "tcp:") { //tcp:127.0.0.1:80\nudp:127.0.0.1:12345 - return &TCP_or_UDPAddr{TCPAddr: ta.(*net.TCPAddr), UDPAddr: ua.(*net.UDPAddr)}, nil + ok, t, u := utils.CommonSplit(s, "tcp", "udp") + + if ok { + return makeTcpUdpAddr(t, u) + } else { + return nil, utils.ErrInvalidData + + } + + } else { + return makeTcpUdpAddr(s, s) + } case UNIX: return net.ResolveUnixAddr(network, s) @@ -694,3 +700,17 @@ func StrToNetAddr(network, s string) (net.Addr, error) { return nil, utils.ErrWrongParameter } } + +func makeTcpUdpAddr(t, u string) (*TCPUDPAddr, error) { + ta, e := StrToNetAddr("tcp", t) + if e != nil { + return nil, e + } + + ua, e := StrToNetAddr("udp", u) + if e != nil { + return nil, e + } + + return &TCPUDPAddr{TCPAddr: ta.(*net.TCPAddr), UDPAddr: ua.(*net.UDPAddr)}, nil +} diff --git a/netLayer/const.go b/netLayer/const.go index 7309e75..b6ac026 100644 --- a/netLayer/const.go +++ b/netLayer/const.go @@ -5,7 +5,7 @@ const ( // Transport Layer Protocols, 使用uint16 mask,所以最多支持16种 TCP uint16 = 1 << iota UDP - Mix //use both tcp and udp + Dual //use both tcp and udp UNIX //unix domain socket IP Raw_socket @@ -21,8 +21,8 @@ func StrToTransportProtocol(s string) uint16 { return TCP case "udp", "udp4", "udp6", "UDP", "UDP4", "UDP6": return UDP - case "mix", "mixed", "Mix", "MIX": - return Mix + case "dual", "mix", "mixed", "Mix", "MIX": + return Dual case "unix", "Unix", "UNIX": return UNIX case "raw", "RAW": diff --git a/proxy/base.go b/proxy/base.go index 80900a1..0ce2dd4 100644 --- a/proxy/base.go +++ b/proxy/base.go @@ -39,7 +39,7 @@ type BaseInterface interface { /////////////////// 传输层 /////////////////// - MultiTransportLayer() bool //同时使用多个传输层来传输数据。direct, socks5 和 shadowsocks 都为true + MultiTransportLayer() bool //同时使用tcp和udp来传输数据。direct, socks5 和 shadowsocks 都为true Network() string //传输层协议,如 tcp, udp, unix, kcp, etc. 这里叫做Network而不是transport, 是遵循 golang 标准包 net包的用法。我们兼容 net的Listen等方法, 可把Network直接作为 net.Listen等方法的 network 参数。 GetXver() int diff --git a/proxy/config_proxy.go b/proxy/config_proxy.go index 90c6dea..aa99361 100644 --- a/proxy/config_proxy.go +++ b/proxy/config_proxy.go @@ -63,12 +63,14 @@ type CommonConf struct { /////////////////// 代理层 /////////////////// - Protocol string `toml:"protocol"` //代理层; 约定,如果一个Protocol尾缀去掉了一个's'后仍然是一个有效协议,则该协议使用了 tls。这种方法继承自 v2simple,适合极简模式 - Uuid string `toml:"uuid"` //代理层用户的唯一标识,视代理层协议而定,一般使用uuid,但trojan协议是随便的password, 而socks5 和 http 则使用 user+pass 的形式。 我们为了简洁、一致,就统一放到了 这个字段里。 - Version int `toml:"version"` //可选,代理层协议版本号,vless v1 要用到。 + Protocol string `toml:"protocol"` //代理层; 约定,如果一个Protocol尾缀去掉了一个's'后仍然是一个有效协议,则该协议使用了 tls。这种方法继承自 v2simple,适合极简模式 + Uuid string `toml:"uuid"` //代理层用户的唯一标识,视代理层协议而定,一般使用uuid,但trojan协议是随便的password, 而socks5 和 http 则使用 user+pass 的形式。 我们为了简洁、一致,就统一放到了 这个字段里。 + Version int `toml:"version"` //可选,代理层协议版本号,vless v1 要用到。 + EncryptAlgo string `toml:"encrypt_algo"` //内部加密算法,vmess/ss 等协议可指定 } +//和 GetAddrStrForListenOrDial 的区别是,它优先使用host,其次再使用ip func (cc *CommonConf) GetAddrStr() string { switch cc.Network { case "unix": diff --git a/proxy/direct.go b/proxy/direct.go index dfab423..82b519f 100644 --- a/proxy/direct.go +++ b/proxy/direct.go @@ -32,12 +32,12 @@ func (DirectCreator) NewClient(dc *DialConf) (Client, error) { d := &DirectClient{} if dc.SendThrough != "" { - st, err := netLayer.StrToNetAddr(netLayer.MixNetworkName, dc.SendThrough) + st, err := netLayer.StrToNetAddr(netLayer.DualNetworkName, dc.SendThrough) if err != nil { return nil, err } - d.localTCPAddr = st.(*netLayer.TCP_or_UDPAddr).TCPAddr - d.localUDPAddr = st.(*netLayer.TCP_or_UDPAddr).UDPAddr + d.localTCPAddr = st.(*netLayer.TCPUDPAddr).TCPAddr + d.localUDPAddr = st.(*netLayer.TCPUDPAddr).UDPAddr } @@ -64,7 +64,7 @@ func (d *DirectClient) Handshake(underlay net.Conn, firstPayload []byte, target if d.Sockopt != nil { if d.localTCPAddr == nil { - result, err = target.DialWithOpt(d.Sockopt, nil) //尽量避免把nil的 *net.TCPAddr 装箱到 net.Addr里 + result, err = target.DialWithOpt(d.Sockopt, nil) //避免把nil的 *net.TCPAddr 装箱到 net.Addr里 } else { result, err = target.DialWithOpt(d.Sockopt, d.localTCPAddr) diff --git a/proxy/http/server.go b/proxy/http/server.go index b6e6f6c..776f9d7 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -96,7 +96,8 @@ type Server struct { *utils.MultiUserMap - OnlyConnect bool //是否仅支持Connect命令; 如果为true, 则直接通过 GET http://xxx 这种请求不再被认为是有效的。之前本以为connect就可以搞定一切,后来实测发现 wget 确实在 非https时 会用 纯http请求的方式 请求代理。所以 一般 OnlyConnect 为 false即可. + OnlyConnect bool //是否仅支持Connect命令; 如果为true, 则直接通过 GET http://xxx 这种请求不再被认为是有效的。 + //之前本以为connect就可以搞定一切,后来实测发现 wget 确实在 非https时 会用 纯http请求的方式 请求代理。所以 一般 OnlyConnect 保持默认 false即可. } func NewServer() *Server { diff --git a/proxy/shadowsocks/shadowsocks.go b/proxy/shadowsocks/shadowsocks.go index 0dcb574..c0d82d4 100644 --- a/proxy/shadowsocks/shadowsocks.go +++ b/proxy/shadowsocks/shadowsocks.go @@ -18,14 +18,6 @@ https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers ss不像vmess等协议一样,只使用一种传输层协议来传输 tcp和udp数据;而是:用tcp传tcp,用udp传udp。 如此的话,特征必很明显。 -还有一个重要的问题,就是,我们vs的架构,在设计之初,就是为vmess/vless/trojan等 只需要一种传输层协议 来获取 多种传输层协议的客户端等数据的; - -而为了支持ss,以目前的vs架构来说,要同时写两个listen,一个监听tcp,一个监听udp,如此才能做到。 - -而且对于client来说也比较棘手,因为我们的架构只认为需要dial单一的传输层协议就可以与一个服务端完整通信,所以配置文件里需要配置network指明使用的是哪个传输层协议;而如果是ss的模式的话,则客户端对tcp和udp都要拨号,也十分麻烦。 - - - 另外,本包是普通的ss AEAD Ciphers ,不过它还是有问题。所以以后要研究ss-2022 https://github.com/shadowsocks/shadowsocks-org/issues/183 @@ -175,27 +167,12 @@ func (ph *MethodPass) InitWithUrl(u *url.URL) bool { //uuid: "method:xxxx\npass:xxxx" func (ph *MethodPass) InitWithStr(str string) (ok bool) { - str = strings.TrimSuffix(str, "\n") - strs := strings.SplitN(str, "\n", 2) - if len(strs) != 2 { - return - } var potentialMethod, potentialPass string - - ustrs := strings.SplitN(strs[0], ":", 2) - if ustrs[0] != "method" { - + ok, potentialMethod, potentialPass = utils.CommonSplit(str, "method", "pass") + if !ok { return } - potentialMethod = ustrs[1] - - pstrs := strings.SplitN(strs[1], ":", 2) - if pstrs[0] != "pass" { - - return - } - potentialPass = pstrs[1] if potentialMethod != "" && potentialPass != "" { ph.Method = potentialMethod diff --git a/proxy/vmess/client.go b/proxy/vmess/client.go index 83e74d6..4371165 100644 --- a/proxy/vmess/client.go +++ b/proxy/vmess/client.go @@ -22,12 +22,31 @@ import ( "golang.org/x/crypto/chacha20poly1305" ) -const vmess_security_confStr string = "vmess_security" - func init() { proxy.RegisterClient(Name, ClientCreator{}) } +const Security_confStr string = "vmess_security" + +func GetEncryptAlgo(dc *proxy.DialConf) (result string) { + + if len(dc.Extra) > 0 { + if thing := dc.Extra[Security_confStr]; thing != nil { + if str, ok := thing.(string); ok { + + result = str + + } + } + } + + if dc.EncryptAlgo != "" { + result = dc.EncryptAlgo + } + + return result +} + type ClientCreator struct{} func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) (*proxy.DialConf, error) { @@ -51,7 +70,7 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) dc.Extra = make(map[string]any) } - dc.Extra[vmess_security_confStr] = security + dc.Extra[Security_confStr] = security } return dc, nil @@ -66,26 +85,11 @@ func (ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) { c.V2rayUser = utils.V2rayUser(uuid) c.opt = OptChunkStream - hasSetSecurityByExtra := false + var ea string = GetEncryptAlgo(dc) - if len(dc.Extra) > 0 { - if thing := dc.Extra[vmess_security_confStr]; thing != nil { - if str, ok := thing.(string); ok { + if err := c.specifySecurityByStr(ea); err != nil { - err = c.specifySecurityByStr(str) - - if err == nil { - hasSetSecurityByExtra = true - } else { - return nil, err - } - - } - } - } - - if !hasSetSecurityByExtra { - c.specifySecurityByStr("") + return nil, err } return c, nil diff --git a/utils/algo.go b/utils/algo.go index dc14115..85251c6 100644 --- a/utils/algo.go +++ b/utils/algo.go @@ -1,6 +1,8 @@ package utils import ( + "strings" + "golang.org/x/exp/constraints" "golang.org/x/exp/slices" ) @@ -89,3 +91,28 @@ func GetMapSortedKeySlice[K constraints.Ordered, V any](theMap map[K]V) []K { return result } + +//本作的惯例, 经常使用如下字符串作为配置: s = "e1:v1\ne2:v2", +func CommonSplit(s, e1, e2 string) (ok bool, v1, v2 string) { + s = strings.TrimSuffix(s, "\n") + lines := strings.SplitN(s, "\n", 2) + if len(lines) != 2 { + return + } + + strs1 := strings.SplitN(lines[0], ":", 2) + if strs1[0] != e1 { + + return + } + v1 = strs1[1] + + strs2 := strings.SplitN(lines[1], ":", 2) + if strs2[0] != e2 { + + return + } + v2 = strs2[1] + ok = true + return +} diff --git a/utils/algo_test.go b/utils/algo_test.go index 927d1b2..a3472d8 100644 --- a/utils/algo_test.go +++ b/utils/algo_test.go @@ -8,6 +8,17 @@ import ( "gonum.org/v1/gonum/stat/combin" ) +func TestCommonSpit(t *testing.T) { + realv1 := "v1" + realv2 := "v2" + s := "e1:" + realv1 + "\ne2:" + realv2 + ok, v1, v2 := utils.CommonSplit(s, "e1", "e2") + + if !ok || realv1 != v1 || realv2 != v2 { + t.FailNow() + } +} + var x = []string{"AA", "BB", "CC", "DD"} var y = []int{1, 2, 3, 4} diff --git a/utils/error.go b/utils/error.go index 33ba47d..952e768 100644 --- a/utils/error.go +++ b/utils/error.go @@ -81,7 +81,13 @@ func (ef ErrBuffer) Unwarp() error { func (ef ErrBuffer) Error() string { - return ef.Err.Error() + ", with Buffer." + if ef.Buf != nil { + return ef.Err.Error() + ", with Buffer,len " + strconv.Itoa(ef.Buf.Len()) + + } else { + return ef.Err.Error() + ", with nil Buffer." + + } } // ErrInErr 很适合一个err包含另一个err,并且提供附带数据的情况. 类似 fmt.Errorf. diff --git a/utils/user.go b/utils/user.go index 6b41dfa..5ebb47a 100644 --- a/utils/user.go +++ b/utils/user.go @@ -3,7 +3,6 @@ package utils import ( "bytes" "net/url" - "strings" "sync" ) @@ -168,33 +167,13 @@ func (ph *UserPass) InitWithUrl(u *url.URL) bool { //uuid: "user:xxxx\npass:xxxx" func (ph *UserPass) InitWithStr(str string) (ok bool) { - str = strings.TrimSuffix(str, "\n") - strs := strings.SplitN(str, "\n", 2) - if len(strs) != 2 { + var v1, v2 string + ok, v1, v2 = CommonSplit(str, "user", "pass") + if !ok { return } - - var potentialUser, potentialPass string - - ustrs := strings.SplitN(strs[0], ":", 2) - if ustrs[0] != "user" { - - return - } - potentialUser = ustrs[1] - - pstrs := strings.SplitN(strs[1], ":", 2) - if pstrs[0] != "pass" { - - return - } - potentialPass = pstrs[1] - - if potentialUser != "" && potentialPass != "" { - ph.UserID = []byte(potentialUser) - ph.Password = []byte(potentialPass) - } - ok = true + ph.UserID = []byte(v1) + ph.Password = []byte(v2) return }