diff --git a/README.md b/README.md index 9630e18..cfe91c3 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,10 @@ verysimple项目大大简化了 转发机制,能提高运行速度。本项目 规定,编译出的文件名必须以 verysimple 开头. -verysimple 研发了一些新技术,可以加速。 +verysimple 研发了一些新技术,可以加速,目前基本上是全网最快。 + +vs的主要亮点是 全协议readv加速,lazy技术,vless v1,hysteria 阻控,更广泛等utls支持,交互模式等。 + 为了不吓跑小白,目前 本 README 把安装、使用方式 放在了前面,如果你要直接阅读本作的技术介绍部分,点击跳转 -> [创新点](#创新点) @@ -221,6 +224,7 @@ openssl req -new -x509 -days 7305 -key cert.key -out cert.pem ## 创新点 本作有不少创新点,如下 + ### 协议 实现了vless协议(v0,v1)和vlesss(即vless+tcp+tls), @@ -420,6 +424,14 @@ https://github.com/hahahrfool/v2ray_simple/discussions 有贡献想法的同学,阅读 [CONTRIBUTING](CONTRIBUTING.md) 或者issue中的【开发者贡献指南】. +#### 开发者入门指导: + +首先学会使用verysimple,熟读本 README.md 和 examples/ 下的配置文件 + +之后读 version.go 文件里的 注释,对本作结构有一个认识。然后读 proxy/doc.go 理解 VSI模型。 + +之后就可以在go doc中选择自己感兴趣的地方阅读了。 + ## 本项目所使用的开源协议 MIT协议,即你用的时候也要附带一个MIT文件,然后作者不承担任何责任、义务、后果。 diff --git a/examples/vlesss.server.toml b/examples/vlesss.server.toml index f745bfb..f7fb3d3 100644 --- a/examples/vlesss.server.toml +++ b/examples/vlesss.server.toml @@ -7,7 +7,8 @@ host = "0.0.0.0" port = 4433 version = 0 insecure = true -fallback = ":80" #默认回落地址.必须是本机ip, 或者unix domain socket的文件名/路径 +fallback = ":80" # 默认回落地址.ip必须是本机ip(可以省略ip而只写端口,程序会默认补全127.0.0.1), 或者unix domain socket的文件名/路径, 或者 udp://127.0.0.1:80 这种url格式。 (用udp以试图回落到 nginx的 无tls 的 http3 服务端口) + cert = "cert.pem" # 这里既可以默认放在程序本身目录下,也可以指定完整路径 key = "cert.key" diff --git a/main.go b/main.go index a21b8c8..64382bf 100644 --- a/main.go +++ b/main.go @@ -286,7 +286,7 @@ func main() { } } -//非阻塞. +//非阻塞. 在main函数中被调用。也可以在 test代码中直接使用 listenSer 函数 来手动开启新的转发流程。 // 若 not_temporary 为true, 则生成的listener将会被添加到 listenerArray 中 func listenSer(inServer proxy.Server, defaultOutClientForThis proxy.Client, not_temporary bool) (thisListener net.Listener) { @@ -411,6 +411,8 @@ type incomingInserverConnState struct { // handleNewIncomeConnection 会处理 网络层至高级层的数据, // 然后将代理层的处理发往 handshakeInserver_and_passToOutClient 函数。 +// +// 在 listenSer 中被调用。 func handleNewIncomeConnection(inServer proxy.Server, defaultClientForThis proxy.Client, thisLocalConnectionInstance net.Conn, forbidDNS_orRoute bool) { iics := incomingInserverConnState{ @@ -593,6 +595,7 @@ startPass: handshakeInserver_and_passToOutClient(iics) } +//被 handshakeInserver_and_passToOutClient 调用 func handshakeInserver(iics *incomingInserverConnState) (wlc io.ReadWriteCloser, udp_wlc netLayer.MsgConn, targetAddr netLayer.Addr, err error) { inServer := iics.inServer wrappedConn := iics.wrappedConn @@ -708,6 +711,8 @@ func handshakeInserver(iics *incomingInserverConnState) (wlc io.ReadWriteCloser, // 本函数 处理inServer的代理层数据,并在试图处理 分流和回落后,将流量导向目标,并开始Copy。 // iics 不使用指针, 因为iics不能公用,因为 在多路复用时 iics.wrappedConn 是会变化的。 +// +//被 handleNewIncomeConnection 调用。 func handshakeInserver_and_passToOutClient(iics incomingInserverConnState) { if iics.shouldFallback { @@ -729,6 +734,7 @@ func handshakeInserver_and_passToOutClient(iics incomingInserverConnState) { } +// 被 passToOutClient 调用 func checkfallback(iics incomingInserverConnState) (targetAddr netLayer.Addr, wlc io.ReadWriteCloser) { //先检查 mainFallback,如果mainFallback中各项都不满足 或者根本没有 mainFallback 再检查 defaultFallback @@ -805,6 +811,7 @@ func checkfallback(iics incomingInserverConnState) (targetAddr netLayer.Addr, wl return } +//被 handshakeInserver_and_passToOutClient 和 handshakeInserver 的innerMux部分 调用,会调用 dialClient_andRelay func passToOutClient(iics incomingInserverConnState, err error, gotoFallback bool, wlc io.ReadWriteCloser, udp_wlc netLayer.MsgConn, targetAddr netLayer.Addr) { wrappedConn := iics.wrappedConn @@ -1021,8 +1028,9 @@ func passToOutClient(iics incomingInserverConnState, err error, gotoFallback boo dialClient_andRelay(iics, targetAddr, client, isTlsLazy_clientEnd, wlc, udp_wlc) } -//dialClient 对实际client进行拨号,处理传输层, tls层, 高级层等所有层级后,进行代理层握手 -// result = 0 表示拨号成功, result = -1 表示 拨号失败, result = 1 表示 拨号成功并处理了转发阶段(用于lazy和mux ) +//dialClient 对实际client进行拨号,处理传输层, tls层, 高级层等所有层级后,进行代理层握手。 +// result = 0 表示拨号成功, result = -1 表示 拨号失败, result = 1 表示 拨号成功 并 已经自行处理了转发阶段(用于lazy和mux )。 +// 在 dialClient_andRelay 中被调用。 func dialClient(targetAddr netLayer.Addr, client proxy.Client, baseLocalConn, @@ -1403,6 +1411,7 @@ advLayerStep: return } //dialClient +//在 dialClient 中调用 func dialInnerMux(client proxy.Client, wrc io.ReadWriteCloser, innerProxyName string, targetAddr netLayer.Addr, isudp bool) (realwrc io.ReadWriteCloser, realudp_wrc netLayer.MsgConn, result int) { smuxSession := client.GetClientInnerMuxSession(wrc) if smuxSession == nil { diff --git a/tlsLayer/tlsLayer.go b/tlsLayer/tlsLayer.go index 8a253d7..1fe17d9 100644 --- a/tlsLayer/tlsLayer.go +++ b/tlsLayer/tlsLayer.go @@ -19,11 +19,14 @@ import ( "github.com/hahahrfool/v2ray_simple/utils" ) +//使用 ecc p256 方式生成证书 func GenerateRandomeCert_Key() ([]byte, []byte) { - //ecc p256 max := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, _ := rand.Int(rand.Reader, max) + + //可参考 https://blog.ideawand.com/2017/11/22/build-certificate-that-support-Subject-Alternative-Name-SAN/ + subject := pkix.Name{ Country: []string{"ZZ"}, Province: []string{"asfdsdaf"}, @@ -69,6 +72,7 @@ func GenerateRandomeCert_Key() ([]byte, []byte) { */ } +// 会调用 GenerateRandomeCert_Key 来生成证书,并生成包含该证书的 []tls.Certificate func GenerateRandomTLSCert() []tls.Certificate { tlsCert, err := tls.X509KeyPair(GenerateRandomeCert_Key()) @@ -79,6 +83,7 @@ func GenerateRandomTLSCert() []tls.Certificate { } +// 会调用 GenerateRandomeCert_Key 来生成证书,并输出到文件 func GenerateRandomCertKeyFiles(cfn, kfn string) error { cb, kb := GenerateRandomeCert_Key() @@ -105,7 +110,7 @@ func GenerateRandomCertKeyFiles(cfn, kfn string) error { return nil } -//如 certFile, keyFile 有一项没给出,则会自动生成随机证书 +//若 certFile, keyFile 有一项没给出,则会自动生成随机证书 func GetCertArrayFromFile(certFile, keyFile string) (certArray []tls.Certificate, err error) { if certFile != "" && keyFile != "" { cert, err := tls.LoadX509KeyPair(utils.GetFilePath(certFile), utils.GetFilePath(keyFile)) diff --git a/utils/algo.go b/utils/algo.go index f36c1ec..dc14115 100644 --- a/utils/algo.go +++ b/utils/algo.go @@ -74,11 +74,11 @@ func TrimSlice[T any](a []T, deleteIndex int) []T { //实际上 golang.org/x/exp/slices 的 Delete 函数也可以, 不过我还是觉得我自己的好理解一些 } -func GetMapSortedKeySlice[K constraints.Ordered, V any](flags map[K]V) []K { - result := make([]K, len(flags)) +func GetMapSortedKeySlice[K constraints.Ordered, V any](theMap map[K]V) []K { + result := make([]K, len(theMap)) i := 0 - for f := range flags { + for f := range theMap { result[i] = f i++ } diff --git a/utils/algo_heap.go b/utils/algo_heap.go new file mode 100644 index 0000000..d5f59a5 --- /dev/null +++ b/utils/algo_heap.go @@ -0,0 +1,132 @@ +package utils + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//就是把 container/heap 的代码改成了泛型实现, 并使用了数组来存储数据。 +// 性能来说应该比原来包高一些,因为少了 interface打包和解包 的损耗。 +// 之所以有这个转换,是打算优化一下smux中相关shaper。 + +// The Heap type describes the requirements +// for a type using the routines in this package. +// Any type that implements it may be used as a +// min-heap with the following invariants (established after +// Init has been called or if the data is empty or sorted): +// +// !h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len() +// +// Note that Push and Pop in this interface are for package heap's +// implementation to call. To add and remove things from the heap, +// use heap.Push and heap.Pop. +// +// 实际上我们这个包装已经类似 优先队列了, 至于如何优先取决于 LessFunc +type Heap[T any] struct { + LessFunc func(i, j int, a []T) bool + + Array []T +} + +// Init establishes the heap invariants required by the other routines in this package. +// Init is idempotent with respect to the heap invariants +// and may be called whenever the heap invariants may have been invalidated. +// The complexity is O(n) where n = h.Len(). +func (h *Heap[T]) Init() { + // heapify + n := h.Len() + for i := n/2 - 1; i >= 0; i-- { + h.down(i, n) + } +} + +func (h *Heap[T]) Len() int { + return len(h.Array) +} + +func (h *Heap[T]) rawPush(x T) { + h.Array = append(h.Array, x) +} + +func (h *Heap[T]) rawPop() T { + old := h.Array + n := len(old) + x := old[n-1] + h.Array = old[0 : n-1] + return x +} + +func (h *Heap[T]) swap(i, j int) { + + h.Array[i], h.Array[j] = h.Array[j], h.Array[i] +} + +// Push pushes the element x onto the heap. +// The complexity is O(log n) where n = h.Len(). +func (h *Heap[T]) Push(x T) { + h.rawPush(x) + h.up(h.Len() - 1) +} + +// Pop removes and returns the minimum element (according to Less) from the heap. +// The complexity is O(log n) where n = h.Len(). +// Pop is equivalent to Remove(h, 0). +func (h *Heap[T]) Pop() T { + n := h.Len() - 1 + h.swap(0, n) + h.down(0, n) + return h.rawPop() +} + +// Remove removes and returns the element at index i from the heap. +// The complexity is O(log n) where n = h.Len(). +func (h *Heap[T]) Remove(i int) T { + n := h.Len() - 1 + if n != i { + h.swap(i, n) + if !h.down(i, n) { + h.up(i) + } + } + return h.Pop() +} + +// Fix re-establishes the heap ordering after the element at index i has changed its value. +// Changing the value of the element at index i and then calling Fix is equivalent to, +// but less expensive than, calling Remove(h, i) followed by a Push of the new value. +// The complexity is O(log n) where n = h.Len(). +func (h *Heap[T]) Fix(i int) { + if !h.down(i, h.Len()) { + h.up(i) + } +} + +func (h *Heap[T]) up(j int) { + for { + i := (j - 1) / 2 // parent + if i == j || !h.LessFunc(j, i, h.Array) { + break + } + h.swap(i, j) + j = i + } +} + +func (h *Heap[T]) down(i0, n int) bool { + i := i0 + for { + j1 := 2*i + 1 + if j1 >= n || j1 < 0 { // j1 < 0 after int overflow + break + } + j := j1 // left child + if j2 := j1 + 1; j2 < n && h.LessFunc(j2, j1, h.Array) { + j = j2 // = 2*i + 2 // right child + } + if !h.LessFunc(j, i, h.Array) { + break + } + h.swap(i, j) + i = j + } + return i > i0 +} diff --git a/version.go b/version.go index d035b18..a2730c4 100644 --- a/version.go +++ b/version.go @@ -25,9 +25,9 @@ Structure 本项目结构 main -> proxy.Standard(配置文件) -> netLayer-> tlsLayer -> httpLayer -> advLayer -> proxy. - main中,读取配置文件,生成 Dail、Listen 、 RoutePolicy 和 Fallback等 对象后,开始监听; + main.go 中,读取配置文件,生成 Dail、Listen 、 RoutePolicy 和 Fallback等 对象后,开始监听,并顺便选择性打开交互模式和 apiServer; - 具体调用链 是 listenSer -> handleNewIncomeConnection -> handshakeInserver_and_passToOutClient -> dialClient + 具体 转发过程 的 调用链 是 listenSer -> handleNewIncomeConnection -> handshakeInserver_and_passToOutClient -> handshakeInserver -> passToOutClient -> (checkfallback) -> dialClient_andRelay -> dialClient , netLayer.Relay / netLayer.RelayUDP 用 netLayer操纵路由,用tlsLayer嗅探tls,用httpLayer操纵回落,可选经过ws/grpc, 然后都搞好后,传到proxy,然后就开始转发