mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-12-24 13:27:56 +08:00
修订文档,示例,代码;解决http头层回落代码导致panic的bug;
添加 encrypt_algo 配置; 添加 configAdapter包
This commit is contained in:
62
README.md
62
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了。
|
||||
|
||||
关键不在于谁是作者,一个作者倒下,千万个作者会站起来。
|
||||
|
||||
我们的思想 生生不息,追求自由的人们啊,一起奋斗吧!
|
||||
|
||||
>鱼,我所欲也;熊掌,亦我所欲也。二者不可得兼,舍鱼而取熊掌者也。生,亦我所欲也;义,亦我所欲也。二者不可得兼,舍生而取义者也。
|
||||
|
||||
>砍头不要紧,
|
||||
只要主义真。
|
||||
杀了夏明翰,
|
||||
还有后来人。
|
||||
|
||||
|
||||
|
||||
|
||||
# 免责声明与鸣谢
|
||||
|
||||
|
||||
52
configAdapter/package.go
Normal file
52
configAdapter/package.go
Normal 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 ""
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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数据,属于双栈
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
6
iics.go
6
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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user