mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-12 20:20:17 +08:00
185 lines
5.3 KiB
Go
185 lines
5.3 KiB
Go
package netLayer
|
||
|
||
import (
|
||
"errors"
|
||
"io"
|
||
"net"
|
||
"runtime"
|
||
|
||
"github.com/hahahrfool/v2ray_simple/utils"
|
||
)
|
||
|
||
var errInvalidWrite = errors.New("vless readfrom, invalid write result")
|
||
|
||
const SystemCanSplice = runtime.GOARCH != "wasm" && runtime.GOOS != "windows"
|
||
|
||
type Splicer interface {
|
||
EverPossibleToSplice() bool //是否有机会splice, 如果这个返回false,则永远无法splice; 主要审视自己能否向裸连接写入数据; 读不用splicer担心。
|
||
CanSplice() (bool, net.Conn) //当前状态是否可以splice
|
||
}
|
||
|
||
//这里认为能 splice 或 sendfile的 都算,具体可参考go标准代码的实现, 总之就是tcp和 unix domain socket 可以.
|
||
// 若不是基本Conn,则会试图转换为Splicer并获取底层Conn
|
||
func CanSpliceDirectly(r any) bool {
|
||
|
||
if _, ok := r.(*net.TCPConn); ok {
|
||
return true
|
||
} else if _, ok := r.(*net.UnixConn); ok {
|
||
|
||
return true
|
||
}
|
||
return false
|
||
|
||
}
|
||
|
||
func CanSpliceEventually(r any) bool {
|
||
if s, ok := r.(Splicer); ok {
|
||
return s.EverPossibleToSplice()
|
||
}
|
||
return false
|
||
}
|
||
|
||
//从r读取数据,写入 maySpliceConn 或者 classicWriter, 在条件合适时会使用splice进行加速。
|
||
// 本函数主要应用于裸奔时,一端是socks5/直连,另一端是vless/vless+ws的情况, 因为vless等协议就算裸奔也是要处理一下数据头等情况的, 所以需要进行处理才可裸奔.
|
||
//
|
||
// 注意,splice只有在 maySpliceConn【本身是】或者【变成】 basicConn, 且 r 也是 basicConn时,才会发生。
|
||
// 如果r本身就不是 basicConn,则调用本函数没有意义, 因为既然拿不到basicConn那就不是裸奔,也就不可能splice。
|
||
//
|
||
func TryReadFrom_withSplice(classicWriter io.Writer, maySpliceConn net.Conn, r io.Reader, canDirectFunc func() bool) (written int64, err error) {
|
||
//log.Println("TryReadFrom_withSplice called")
|
||
|
||
underlay_canSpliceDirectly := CanSpliceDirectly(maySpliceConn)
|
||
|
||
var underlay_canSpliceEventually bool
|
||
|
||
if !underlay_canSpliceDirectly {
|
||
underlay_canSpliceEventually = CanSpliceEventually(maySpliceConn)
|
||
}
|
||
|
||
var splicer Splicer
|
||
|
||
if underlay_canSpliceEventually {
|
||
splicer = maySpliceConn.(Splicer)
|
||
}
|
||
|
||
/*
|
||
分多钟情况,
|
||
1. underlay直接是基础连接(underlay_canSpliceDirectly),且现在直接 canDirectFunc 就是true, 此时直接 splice
|
||
2. underlay直接是基础连接(underlay_canSpliceDirectly),但现在的连接阶段还不能直接直连,此时要读写一次然后判断一次,直到 canDirectFunc 变成 true
|
||
3. underlay 不是基础连接,但是 是 Splicer(underlay_canSpliceEventually),且此时我们先等待 underlay已经处于 可直连状态, 即 splicer.CanSplice()变成 true,然后再确保 canDirectFunc 返回true
|
||
4. underlay啥也不是,直接经典拷贝。
|
||
*/
|
||
|
||
if underlay_canSpliceDirectly || underlay_canSpliceEventually {
|
||
if underlay_canSpliceDirectly && canDirectFunc() {
|
||
if rt, ok := maySpliceConn.(io.ReaderFrom); ok {
|
||
return rt.ReadFrom(r)
|
||
} else {
|
||
panic("uc.underlayIsBasic, but can't cast to ReadFrom")
|
||
}
|
||
} else {
|
||
//循环读写,直到 canDirectFunc 和 splicer.CanSplice() 都为true
|
||
|
||
buf := utils.GetPacket()
|
||
defer utils.PutPacket(buf)
|
||
for {
|
||
nr, er := r.Read(buf)
|
||
if nr > 0 {
|
||
nw, ew := classicWriter.Write(buf[0:nr])
|
||
if nw < 0 || nr < nw {
|
||
nw = 0
|
||
if ew == nil {
|
||
ew = errInvalidWrite
|
||
}
|
||
}
|
||
written += int64(nw)
|
||
if ew != nil {
|
||
err = ew
|
||
break
|
||
}
|
||
if nr != nw {
|
||
err = io.ErrShortWrite
|
||
break
|
||
}
|
||
}
|
||
if er != nil {
|
||
err = er
|
||
break
|
||
}
|
||
|
||
if underlay_canSpliceDirectly {
|
||
if rt, ok := maySpliceConn.(io.ReaderFrom); ok {
|
||
var readfromN int64
|
||
readfromN, err = rt.ReadFrom(r)
|
||
written += readfromN
|
||
return
|
||
} else {
|
||
panic("uc.underlayIsBasic, but can't cast to ReadFrom")
|
||
}
|
||
} else {
|
||
|
||
if canStartSplice, rawConn := splicer.CanSplice(); canStartSplice {
|
||
if rt, ok := rawConn.(io.ReaderFrom); ok {
|
||
var readfromN int64
|
||
readfromN, err = rt.ReadFrom(r)
|
||
written += readfromN
|
||
return
|
||
} else {
|
||
panic("uc.underlayIsBasic, but can't cast to ReadFrom")
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
} //for read
|
||
return
|
||
|
||
} //cant direct write
|
||
|
||
} else { //splice not possible, 仅仅循环读写即可
|
||
|
||
// 我们的vless的ReadFrom方法使用到了本TryReadFrom_withSplice函数, 所以本函数的内部不可再使用ReadFrom作为回落选项
|
||
// ReadFrom会调用 io.CopyBuffer 而 io.CopyBuffer 是又会ReadFrom的,所以说我们调用 ReadFrom 那就会造成无限递归然后栈溢出闪退。
|
||
|
||
//所以我们只能单独把纯粹的经典拷贝代码拿出来使用
|
||
|
||
return ClassicCopy(classicWriter, r)
|
||
}
|
||
|
||
}
|
||
|
||
//拷贝自 io.CopyBuffer。 因为原始的 CopyBuffer会又调用ReadFrom, 而我们这里过滤掉了ReadFrom, 希望直接进行经典拷贝
|
||
func ClassicCopy(w io.Writer, r io.Reader) (written int64, err error) {
|
||
buf := utils.GetPacket()
|
||
defer utils.PutPacket(buf)
|
||
for {
|
||
|
||
nr, er := r.Read(buf)
|
||
if nr > 0 {
|
||
nw, ew := w.Write(buf[0:nr])
|
||
|
||
if nw < 0 || nr < nw {
|
||
nw = 0
|
||
if ew == nil {
|
||
ew = errInvalidWrite
|
||
}
|
||
}
|
||
written += int64(nw)
|
||
if ew != nil {
|
||
err = ew
|
||
break
|
||
}
|
||
if nr != nw {
|
||
err = io.ErrShortWrite
|
||
break
|
||
}
|
||
}
|
||
if er != nil {
|
||
|
||
err = er
|
||
break
|
||
}
|
||
}
|
||
return
|
||
}
|