修订文档,示例,代码;解决http头层回落代码导致panic的bug;

添加 encrypt_algo 配置;

添加 configAdapter包
This commit is contained in:
e1732a364fed
2000-01-01 00:00:00 +00:00
parent 0546be296e
commit 2e5a33feb1
20 changed files with 247 additions and 165 deletions

View File

@@ -30,17 +30,20 @@ verysimple 研发了一些新技术,使用自研架构,可以加速,目前
vs的一些亮点是 全协议readv加速lazy技术vless v1hysteria 阻控更广泛的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了。
关键不在于谁是作者,一个作者倒下,千万个作者会站起来。
我们的思想 生生不息,追求自由的人们啊,一起奋斗吧!
>鱼,我所欲也;熊掌,亦我所欲也。二者不可得兼,舍鱼而取熊掌者也。生,亦我所欲也;义,亦我所欲也。二者不可得兼,舍生而取义者也。
>砍头不要紧,
只要主义真。
杀了夏明翰,
还有后来人。
# 免责声明与鸣谢

52
configAdapter/package.go Normal file
View File

@@ -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 ""
}

View File

@@ -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"
toTag = "direct"

View File

@@ -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"
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数据属于双栈

View File

@@ -1,6 +1,7 @@
[[listen]]
protocol = "shadowsocks"
uuid = "method:AES-128-GCM\npass:iloveverysimple"
host = "127.0.0.1"
port = 4434
#network = "mix"
uuid = "method:AES-128-GCM\npass:iloveverysimple"
#network = "dual"

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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":

View File

@@ -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

View File

@@ -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":

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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}

View File

@@ -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.

View File

@@ -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
}