mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-06 17:27:05 +08:00

现在整个程序均通过了go test, main 也可以正常运行了。 Relay_UDP 函数添加流量计数; 发现之前 Relay函数的流量计数 在main.go里参数传反了,导致实际上计数的是上传而不是下载,已修复 对fullcone的情况做了特别考量。MsgConn的 Close函数在fullcone时不能随便被调用。 因此我添加了一个 CloseConnWithRaddr(raddr Addr) error 方法,以及 Fullcone() bool 方法 在utils包的init部分使用 rand 随机种子
281 lines
6.5 KiB
Go
281 lines
6.5 KiB
Go
package netLayer
|
||
|
||
import (
|
||
"net"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/hahahrfool/v2ray_simple/utils"
|
||
)
|
||
|
||
const (
|
||
MaxUDP_packetLen = 64 * 1024 // 关于 udp包数据长度,可参考 https://cloud.tencent.com/developer/article/1021196
|
||
|
||
)
|
||
|
||
var (
|
||
//udp不能无限监听, 否则每一个udp申请都对应打开了一个本地udp端口,一直监听的话时间一长,就会导致 too many open files
|
||
// 因为实际上udp在网页代理中主要用于dns请求, 所以不妨设的小一点。
|
||
// 放心,只要能持续不断地从远程服务器收到数据, 建立的udp连接就会持续地更新Deadline 而续命一段时间.
|
||
UDP_timeout = time.Minute * 3
|
||
)
|
||
|
||
//本文件内含 一些 转发 udp 数据的 接口与方法
|
||
|
||
//MsgConn一般用于 udp. 是一种类似 net.PacketConn 的包装
|
||
//
|
||
//使用Addr,是因为有可能申请的是域名,而不是ip
|
||
type MsgConn interface {
|
||
ReadFrom() ([]byte, Addr, error)
|
||
WriteTo([]byte, Addr) error
|
||
CloseConnWithRaddr(raddr Addr) error //关闭特定连接
|
||
Close() error //关闭所有连接
|
||
Fullcone() bool //若Fullcone, 则在转发因另一端关闭而结束后, RelayUDP函数不会Close它.
|
||
}
|
||
|
||
// 阻塞. 返回从 rc 下载的总字节数. 拷贝完成后自动关闭双端连接.
|
||
func RelayUDP(rc, lc MsgConn) int {
|
||
|
||
//在转发时, 有可能有多种情况
|
||
/*
|
||
1. dokodemo 监听udp 定向 导向到 direct 的远程udp实际地址
|
||
此时因为是定向的, 所以肯定不是fullcone
|
||
|
||
dokodemo 用的是 UniTargetMsgConn, underlay 是 netLayer.UDPConn, 其已经设置了UDP_timeout
|
||
|
||
在 netLayer.UDPConn 超时后, ReadFrom 就会解放, 并触发双向Close, 来关闭我们的 direct的udp连接。
|
||
|
||
1.5. 比较少见的情况, dokodemo监听tcp, 然后发送到 direct 的udp. 此时客户应用程序可以手动关闭tcp连接来帮我们触发 udp连接 的 close
|
||
|
||
2. socks5监听 udp, 导向到 direct 的远程udp实际地址
|
||
|
||
socks5端只用一个udp连接来监听所有信息, 所以不能关闭, 所以没有设置超时
|
||
|
||
此时我们需要对 每一个 direct的udp连接 设置超时, 否则就会一直占用端口
|
||
|
||
3. socks5 监听udp, 导向到 trojan, 然后 服务端的 trojan 再导向 direct
|
||
|
||
trojan 也是用一个信道来接收udp的所有请求的, 所以trojan的连接也不能关.
|
||
|
||
所以依然需要在服务端 的 direct上面 加Read 时限
|
||
|
||
否则 rc.ReadFrom() 会卡住而不返回.
|
||
|
||
因为direct 使用 UDPMsgConnWrapper,而我们已经在 UDPMsgConnWrapper里加了这个逻辑, 所以可以放心了.
|
||
|
||
4. fullcone, 此时不能对整个监听端口进行close,会影响其它外部链接发来的连接。
|
||
|
||
*/
|
||
|
||
go func() {
|
||
for {
|
||
bs, raddr, err := lc.ReadFrom()
|
||
if err != nil {
|
||
break
|
||
}
|
||
err = rc.WriteTo(bs, raddr)
|
||
if err != nil {
|
||
|
||
break
|
||
}
|
||
}
|
||
if !rc.Fullcone() {
|
||
rc.Close()
|
||
}
|
||
|
||
if !lc.Fullcone() {
|
||
lc.Close()
|
||
}
|
||
|
||
}()
|
||
|
||
count := 0
|
||
|
||
for {
|
||
bs, raddr, err := rc.ReadFrom()
|
||
if err != nil {
|
||
|
||
break
|
||
}
|
||
err = lc.WriteTo(bs, raddr)
|
||
if err != nil {
|
||
|
||
break
|
||
}
|
||
count += len(bs)
|
||
}
|
||
if !rc.Fullcone() {
|
||
rc.Close()
|
||
}
|
||
|
||
if !lc.Fullcone() {
|
||
lc.Close()
|
||
}
|
||
return count
|
||
}
|
||
|
||
// symmetric, proxy/dokodemo 有用到.
|
||
type UniTargetMsgConn struct {
|
||
net.Conn
|
||
target Addr
|
||
}
|
||
|
||
func (u UniTargetMsgConn) Fullcone() bool {
|
||
return false
|
||
}
|
||
|
||
func (u UniTargetMsgConn) ReadFrom() ([]byte, Addr, error) {
|
||
bs := utils.GetPacket()
|
||
|
||
n, err := u.Conn.Read(bs)
|
||
if err != nil {
|
||
return nil, Addr{}, err
|
||
}
|
||
return bs[:n], u.target, err
|
||
}
|
||
|
||
func (u UniTargetMsgConn) WriteTo(bs []byte, _ Addr) error {
|
||
_, err := u.Conn.Write(bs)
|
||
return err
|
||
}
|
||
|
||
func (u UniTargetMsgConn) CloseConnWithRaddr(raddr Addr) error {
|
||
return u.Conn.Close()
|
||
}
|
||
|
||
func (u UniTargetMsgConn) Close() error {
|
||
return u.Conn.Close()
|
||
}
|
||
|
||
//可满足fullcone, 由 Fullcone 的值决定. 在proxy/direct 被用到.
|
||
//
|
||
type UDPMsgConnWrapper struct {
|
||
conn *net.UDPConn
|
||
IsServer bool
|
||
fullcone bool
|
||
|
||
symmetricMap map[HashableAddr]*net.UDPConn
|
||
symmetricMapMutex sync.RWMutex
|
||
}
|
||
|
||
//使用传入的laddr监听udp; 若未给出laddr, 使用一个随机端口监听
|
||
func NewUDPMsgConnClientWrapper(laddr *net.UDPAddr, fullcone bool, isserver bool) *UDPMsgConnWrapper {
|
||
uc := new(UDPMsgConnWrapper)
|
||
|
||
//if laddr == nil {
|
||
// laddr, _ = net.ResolveUDPAddr("udp", ":"+RandPortStr())
|
||
//}
|
||
|
||
udpConn, _ := net.ListenUDP("udp", laddr)
|
||
|
||
uc.conn = udpConn
|
||
uc.fullcone = fullcone
|
||
uc.IsServer = isserver
|
||
if !fullcone {
|
||
uc.symmetricMap = make(map[HashableAddr]*net.UDPConn)
|
||
}
|
||
return uc
|
||
}
|
||
|
||
func (u *UDPMsgConnWrapper) Fullcone() bool {
|
||
return u.fullcone
|
||
}
|
||
|
||
func (u *UDPMsgConnWrapper) ReadFrom() ([]byte, Addr, error) {
|
||
bs := utils.GetPacket()
|
||
|
||
if !u.fullcone {
|
||
//如果不是fullcone, 则我们需要限时关闭
|
||
|
||
u.conn.SetReadDeadline(time.Now().Add(UDP_timeout))
|
||
}
|
||
|
||
n, ad, err := u.conn.ReadFromUDP(bs)
|
||
if err != nil {
|
||
return nil, Addr{}, err
|
||
}
|
||
if !u.fullcone {
|
||
//既然读到了, 那么就取消限时
|
||
u.conn.SetReadDeadline(time.Time{})
|
||
}
|
||
|
||
return bs[:n], NewAddrFromUDPAddr(ad), nil
|
||
}
|
||
|
||
func (u *UDPMsgConnWrapper) WriteTo(bs []byte, raddr Addr) error {
|
||
|
||
if !u.fullcone && !u.IsServer {
|
||
//非fullcone时, 强制 symmetryc, 对每个远程地址 都使用一个 对应的新laddr
|
||
|
||
thishash := raddr.GetHashable()
|
||
|
||
if len(u.symmetricMap) == 0 {
|
||
|
||
_, err := u.conn.WriteTo(bs, raddr.ToUDPAddr())
|
||
if err == nil {
|
||
u.symmetricMapMutex.Lock()
|
||
u.symmetricMap[thishash] = u.conn
|
||
u.symmetricMapMutex.Unlock()
|
||
}
|
||
return err
|
||
}
|
||
|
||
u.symmetricMapMutex.RLock()
|
||
theConn := u.symmetricMap[thishash]
|
||
u.symmetricMapMutex.RUnlock()
|
||
|
||
if theConn == nil {
|
||
var e error
|
||
theConn, e = net.ListenUDP("udp", nil)
|
||
if e != nil {
|
||
return e
|
||
}
|
||
|
||
u.symmetricMapMutex.Lock()
|
||
u.symmetricMap[thishash] = theConn
|
||
u.symmetricMapMutex.Unlock()
|
||
}
|
||
|
||
_, err := theConn.WriteTo(bs, raddr.ToUDPAddr())
|
||
return err
|
||
|
||
} else {
|
||
_, err := u.conn.WriteTo(bs, raddr.ToUDPAddr())
|
||
return err
|
||
|
||
}
|
||
}
|
||
|
||
func (u *UDPMsgConnWrapper) CloseConnWithRaddr(raddr Addr) error {
|
||
if !u.IsServer {
|
||
if u.fullcone {
|
||
u.conn.SetReadDeadline(time.Now())
|
||
|
||
} else {
|
||
u.symmetricMapMutex.Lock()
|
||
|
||
thehash := raddr.GetHashable()
|
||
theConn := u.symmetricMap[thehash]
|
||
|
||
if theConn != nil {
|
||
delete(u.symmetricMap, thehash)
|
||
theConn.Close()
|
||
|
||
}
|
||
|
||
u.symmetricMapMutex.Unlock()
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (u *UDPMsgConnWrapper) Close() error {
|
||
if !u.IsServer && u.fullcone {
|
||
//Close一般只用于关闭客户端、非fullcone的情况, 因为只有这种情况下,才会有 一个 u仅与一个 raddr对话 的清醒.
|
||
|
||
return u.conn.Close()
|
||
} else {
|
||
return nil
|
||
}
|
||
}
|