mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-09-27 05:05:53 +08:00
387 lines
8.7 KiB
Go
387 lines
8.7 KiB
Go
package netLayer
|
||
|
||
import (
|
||
"io"
|
||
"net"
|
||
"reflect"
|
||
"syscall"
|
||
|
||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
func CanWEverSplice(writeConn io.Writer) (wCanSplice bool) {
|
||
wCanSpliceDirectly := CanWSplice(writeConn)
|
||
if wCanSpliceDirectly {
|
||
wCanSplice = true
|
||
} else {
|
||
if CanSpliceEventually(writeConn) {
|
||
wCanSplice = true
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
// TryCopy 尝试 循环 从 readConn 读取数据并写入 writeConn, 直到错误发生。
|
||
// 会接连尝试 splice、循环readv 以及 原始Copy方法。如果 UseReadv 的值为false,则不会使用readv。
|
||
//
|
||
// identity只用于debug 日志输出.
|
||
func TryCopy(writeConn io.Writer, readConn io.Reader, id uint32) (allnum int64, err error) {
|
||
var multiWriter utils.MultiWriter
|
||
|
||
var rawReadConn syscall.RawConn
|
||
var isWriteConn_MultiWriter bool
|
||
|
||
var mr utils.MultiReader
|
||
var br utils.BuffersReader
|
||
|
||
var userlevel_readv bool
|
||
|
||
readvType := 0
|
||
|
||
if ce := utils.CanLogDebug("TryCopy"); ce != nil {
|
||
ce.Write(
|
||
zap.Uint32("id", id),
|
||
zap.String("from", reflect.TypeOf(readConn).String()),
|
||
zap.String("->", reflect.TypeOf(writeConn).String()),
|
||
)
|
||
}
|
||
|
||
if SystemCanSplice {
|
||
|
||
rCanSplice := CanRSplice(readConn)
|
||
wCanSplice := CanWEverSplice(writeConn)
|
||
|
||
if wCanSplice {
|
||
if rCanSplice {
|
||
|
||
if ce := utils.CanLogDebug("copying with splice"); ce != nil {
|
||
ce.Write(zap.Uint32("id", id))
|
||
}
|
||
|
||
goto copy
|
||
} else if sr, ok := (readConn).(SpliceReader); ok && sr != nil {
|
||
if sr.EverPossibleToSpliceRead() {
|
||
|
||
if ce := utils.CanLogDebug("copying with splice, waiting spliceReader"); ce != nil {
|
||
ce.Write(zap.Uint32("id", id))
|
||
}
|
||
|
||
for {
|
||
canRS, tcpConn, unixConn := sr.CanSpliceRead()
|
||
if canRS {
|
||
if tcpConn != nil {
|
||
readConn = tcpConn
|
||
} else if unixConn != nil {
|
||
readConn = unixConn
|
||
}
|
||
|
||
if ce := utils.CanLogDebug("copying with splice, spliceReader ok"); ce != nil {
|
||
ce.Write(zap.Uint32("id", id))
|
||
}
|
||
|
||
goto copy
|
||
}
|
||
//在没能得到用于spliceRead的conn的时候,先普通拷贝
|
||
bs := utils.GetPacket()
|
||
n, err1 := readConn.Read(bs)
|
||
if err1 != nil {
|
||
err = err1
|
||
return
|
||
}
|
||
n2, err2 := writeConn.Write(bs[:n])
|
||
allnum += int64(n2)
|
||
if err2 != nil {
|
||
err = err2
|
||
return
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
// 不全 支持splice的话,我们就考虑 read端 可 readv 的情况
|
||
// 连readv都不让 那就直接 经典拷贝
|
||
if !UseReadv {
|
||
goto classic
|
||
}
|
||
|
||
rawReadConn = GetRawConn(readConn)
|
||
|
||
if rawReadConn == nil {
|
||
var ok bool
|
||
mr, ok = readConn.(utils.MultiReader)
|
||
if ok {
|
||
|
||
readvType = mr.WillReadBuffersBenifit()
|
||
if readvType != 0 {
|
||
|
||
userlevel_readv = true
|
||
|
||
} else {
|
||
goto classic
|
||
}
|
||
} else {
|
||
goto classic
|
||
|
||
}
|
||
}
|
||
|
||
if !IsBasicConn(writeConn) {
|
||
|
||
multiWriter, isWriteConn_MultiWriter = writeConn.(utils.MultiWriter)
|
||
|
||
if userlevel_readv && !isWriteConn_MultiWriter {
|
||
goto classic
|
||
}
|
||
}
|
||
|
||
if ce := utils.CanLogDebug("copying with readv"); ce != nil {
|
||
if userlevel_readv {
|
||
ce.Write(zap.Uint32("id", id),
|
||
zap.String("with", "buffersReader"))
|
||
|
||
} else {
|
||
ce.Write(zap.Uint32("id", id))
|
||
|
||
}
|
||
}
|
||
|
||
{
|
||
var readv_mem *utils.ReadvMem
|
||
|
||
if !userlevel_readv {
|
||
readv_mem = utils.Get_readvMem()
|
||
defer utils.Put_readvMem(readv_mem)
|
||
} else {
|
||
//循环读写直到 CanMultiRead 返回 true
|
||
bs := utils.GetPacket()
|
||
for {
|
||
|
||
if mr.CanMultiRead() {
|
||
break
|
||
}
|
||
var n int
|
||
n, err = readConn.Read(bs)
|
||
if err != nil {
|
||
return
|
||
}
|
||
n, err = writeConn.Write(bs[:n])
|
||
|
||
allnum += int64(n)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
|
||
utils.PutPacket(bs)
|
||
|
||
if readvType == 2 {
|
||
userlevel_readv = false
|
||
rawReadConn = readConn.(utils.Readver).GetRawForReadv()
|
||
|
||
} else {
|
||
br = readConn.(utils.BuffersReader)
|
||
}
|
||
}
|
||
|
||
//这个for循环 只会通过 return 跳出, 不会落到外部
|
||
for {
|
||
var buffers net.Buffers
|
||
|
||
//自此 userlevel_readv 将被当作 是否使用 ReadBuffers 的标识
|
||
|
||
if userlevel_readv {
|
||
buffers, err = br.ReadBuffers()
|
||
|
||
} else {
|
||
buffers, err = utils.ReadvFrom(rawReadConn, readv_mem)
|
||
|
||
}
|
||
|
||
if err != nil {
|
||
return
|
||
}
|
||
var thisWriteNum int64
|
||
var writeErr error
|
||
|
||
// vless/trojan.UserTCPConn, ws.Conn 实现了 utils.MultiWriter
|
||
// vless/trojan的 UserTCPConn 会 间接调用 ws.Conn 的 WriteBuffers
|
||
|
||
if isWriteConn_MultiWriter {
|
||
thisWriteNum, writeErr = multiWriter.WriteBuffers(buffers)
|
||
|
||
} else {
|
||
// 这里不能直接使用 buffers.WriteTo, 因为它会修改buffer本身
|
||
// 而我们为了缓存,是不能允许篡改的
|
||
// 所以我们在确保 writeConn 不是 基本连接后, 要 自行write
|
||
|
||
//在basic时之所以可以 WriteTo,是因为它并不会用循环读取方式, 而是用底层的writev,
|
||
// 而writev时是不会篡改 buffers的
|
||
|
||
//然而经实测,writev会篡改我们的buffers,会导致问题. 而且writev也毫无性能优势,
|
||
//所以这里统一使用我们自己的函数
|
||
|
||
thisWriteNum, writeErr = utils.BuffersWriteTo(buffers, writeConn)
|
||
|
||
}
|
||
|
||
allnum += thisWriteNum
|
||
if writeErr != nil {
|
||
err = writeErr
|
||
return
|
||
}
|
||
|
||
if userlevel_readv {
|
||
br.PutBuffers(buffers)
|
||
|
||
} else {
|
||
buffers = utils.RecoverBuffers(buffers, utils.Readv_buffer_allocLen, utils.ReadvSingleBufLen)
|
||
}
|
||
|
||
}
|
||
}
|
||
classic:
|
||
if ce := utils.CanLogDebug("copying with classic method"); ce != nil {
|
||
ce.Write(zap.Uint32("id", id))
|
||
}
|
||
copy:
|
||
|
||
//Copy内部实现 会调用 ReadFrom, 而ReadFrom 会自动进行splice,
|
||
// 若无splice实现则直接使用原始方法 “循环读取 并 写入”
|
||
// 我们的 vless/trojan 和 ws 的Conn均实现了ReadFrom方法,可以最终splice
|
||
return io.Copy(writeConn, readConn)
|
||
}
|
||
|
||
// 类似TryCopy,但是只会读写一次; 因为只读写一次,所以没办法splice
|
||
func TryCopyOnce(writeConn io.Writer, readConn io.Reader) (allnum int64, err error) {
|
||
var buffers net.Buffers
|
||
var rawConn syscall.RawConn
|
||
|
||
var rm *utils.ReadvMem
|
||
|
||
if ce := utils.CanLogDebug("TryCopy"); ce != nil {
|
||
ce.Write(
|
||
zap.String("from", reflect.TypeOf(readConn).String()),
|
||
zap.String("->", reflect.TypeOf(writeConn).String()),
|
||
)
|
||
}
|
||
|
||
// 不全 支持splice的话,我们就考虑 read端 可 readv 的情况
|
||
// 连readv都不让 那就直接 经典拷贝
|
||
if !UseReadv {
|
||
goto classic
|
||
}
|
||
|
||
rawConn = GetRawConn(readConn)
|
||
|
||
if rawConn == nil {
|
||
goto classic
|
||
}
|
||
|
||
if ce := utils.CanLogDebug("copying with readv"); ce != nil {
|
||
ce.Write()
|
||
}
|
||
|
||
rm = utils.Get_readvMem()
|
||
defer utils.Put_readvMem(rm)
|
||
|
||
buffers, err = utils.ReadvFrom(rawConn, rm)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
allnum, err = utils.BuffersWriteTo(buffers, writeConn) //buffers.WriteTo(writeConn)
|
||
|
||
return
|
||
|
||
classic:
|
||
if ce := utils.CanLogDebug("copying with classic method"); ce != nil {
|
||
ce.Write()
|
||
}
|
||
|
||
bs := utils.GetPacket()
|
||
n, e := readConn.Read(bs)
|
||
if e != nil {
|
||
utils.PutPacket(bs)
|
||
return 0, e
|
||
}
|
||
n, e = writeConn.Write(bs[:n])
|
||
utils.PutPacket(bs)
|
||
return int64(n), e
|
||
}
|
||
|
||
// 从 rc 读取 写入到 lc ,并同时从 lc 读取写入 rc.
|
||
// 阻塞. rc是指 remoteConn, lc 是指localConn; 一般lc由自己监听的Accept产生, rc 由自己拨号产生.
|
||
// UseReadv==true 时 内部使用 TryCopy 进行拷贝,
|
||
// 会自动优选 splice,readv,不行则使用经典拷贝.
|
||
//
|
||
// 拷贝完成后会主动关闭双方连接.
|
||
// 返回从 rc读取到的总字节长度(即下载流量). 如果 downloadByteCount, uploadByteCount 给出,
|
||
// 则会 分别原子更新 上传和下载的总字节数. identity 用于输出日志。
|
||
func Relay(realTargetAddr *Addr, rc, lc io.ReadWriteCloser, identity uint32, downloadByteCount, uploadByteCount *uint64) int64 {
|
||
|
||
if utils.LogLevel == utils.Log_debug {
|
||
|
||
rtaddrStr := realTargetAddr.String()
|
||
go func() {
|
||
n, e := TryCopy(rc, lc, identity)
|
||
|
||
utils.CanLogDebug("Relay End").Write(zap.Uint32("id", identity),
|
||
zap.String("direction", "L->R"),
|
||
zap.String("target", rtaddrStr),
|
||
zap.Int64("bytes", n),
|
||
zap.Error(e),
|
||
)
|
||
|
||
lc.Close()
|
||
rc.Close()
|
||
|
||
if uploadByteCount != nil {
|
||
utils.AtomicAddUint64(uploadByteCount, uint64(n))
|
||
}
|
||
|
||
}()
|
||
|
||
n, e := TryCopy(lc, rc, identity)
|
||
|
||
utils.CanLogDebug("Relay End").Write(zap.Uint32("id", identity),
|
||
zap.String("direction", "R->L"),
|
||
zap.String("target", rtaddrStr),
|
||
zap.Int64("bytes", n),
|
||
zap.Error(e),
|
||
)
|
||
|
||
lc.Close()
|
||
rc.Close()
|
||
|
||
if downloadByteCount != nil {
|
||
utils.AtomicAddUint64(downloadByteCount, uint64(n))
|
||
}
|
||
return n
|
||
} else {
|
||
go func() {
|
||
n, _ := TryCopy(rc, lc, identity)
|
||
|
||
lc.Close()
|
||
rc.Close()
|
||
|
||
if uploadByteCount != nil {
|
||
utils.AtomicAddUint64(uploadByteCount, uint64(n))
|
||
}
|
||
|
||
}()
|
||
|
||
n, _ := TryCopy(lc, rc, identity)
|
||
|
||
lc.Close()
|
||
rc.Close()
|
||
|
||
if downloadByteCount != nil {
|
||
utils.AtomicAddUint64(downloadByteCount, uint64(n))
|
||
}
|
||
return n
|
||
}
|
||
|
||
}
|