Files
v2ray_simple/proxy/direct/client.go
2022-03-18 22:24:10 +08:00

140 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package direct provies direct proxy support for proxy.Client
package direct
import (
"io"
"net"
"net/url"
"sync"
"github.com/hahahrfool/v2ray_simple/config"
"github.com/hahahrfool/v2ray_simple/netLayer"
"github.com/hahahrfool/v2ray_simple/proxy"
)
const name = "direct"
func init() {
proxy.RegisterClientWithURL(name, &ClientCreator{})
}
type Direct struct {
proxy.ProxyCommonStruct
*proxy.UDP_Pipe
targetAddr *netLayer.Addr
addrStr string
}
type ClientCreator struct{}
func NewClient() (proxy.Client, error) {
d := &Direct{
UDP_Pipe: proxy.NewUDP_Pipe(),
}
go RelayUDP_to_Direct(d.UDP_Pipe)
return d, nil
}
func (_ ClientCreator) NewClientFromURL(*url.URL) (proxy.Client, error) {
return NewClient()
}
func (_ ClientCreator) NewClient(*config.DialConf, map[string]interface{}) (proxy.Client, error) {
return NewClient()
}
func (d *Direct) Name() string { return name }
func (d *Direct) Handshake(underlay net.Conn, target *netLayer.Addr) (io.ReadWriter, error) {
if underlay == nil {
d.targetAddr = target
d.SetAddrStr(d.targetAddr.String())
return target.Dial()
}
return underlay, nil
}
// RelayUDP_to_Direct 用于 从一个未知协议读取 udp请求然后通过 直接的udp连接 发送到 远程udp 地址。
// 该函数是阻塞的。而且实现了fullcone; 本函数会直接处理 对外新udp 的dial
//
// RelayUDP_to_Direct 与 RelayTCP 函数 的区别是已经建立的udpConn是可以向其它目的地址发送信息的
// 服务端可以向 客户端发送 非客户端发送过数据 的地址 发来的信息
// 原理是,客户端请求第一次后,就会在服务端开放一个端口,然后其它远程主机就会发现这个端口并试图向客户端发送数据
// 而由于fullcone所以如果客户端要请求一个 不同的udp地址的话如果这个udp地址是之前发送来过信息那么就要用之前建立过的udp连接这样才能保证端口一致
//
func RelayUDP_to_Direct(extractor proxy.UDP_Extractor) {
//具体实现: 每当有对新远程udp地址的请求发生时就会同时 监听 “用于发送该请求到远程udp主机的本地udp端口”接受一切发往 该端口的数据
var dialedUDPConnMap map[string]*net.UDPConn = make(map[string]*net.UDPConn)
var mutex sync.RWMutex
for {
addr, requestData, err := extractor.GetNewUDPRequest()
if err != nil {
break
}
addrStr := addr.String()
mutex.RLock()
oldConn := dialedUDPConnMap[addrStr]
mutex.RUnlock()
if oldConn != nil {
oldConn.Write(requestData)
} else {
newConn, err := net.DialUDP("udp", nil, addr)
if err != nil {
break
}
_, err = newConn.Write(requestData)
if err != nil {
break
}
mutex.Lock()
dialedUDPConnMap[addrStr] = newConn
mutex.Unlock()
//监听所有发往 newConn的 远程任意主机 发来的消息。
go func(thisconn *net.UDPConn, supposedRemoteAddr *net.UDPAddr) {
bs := make([]byte, proxy.MaxUDP_packetLen)
for {
n, raddr, err := thisconn.ReadFromUDP(bs)
if err != nil {
break
}
// 这个远程 地址 无论是新的还是旧的, 都是要 和 newConn关联的下一次向 这个远程地址发消息时,也要用 newConn来发而不是新dial一个。
// 因为判断本身也要占一个语句,所以就不管新旧了,直接赋值即可。
// 所以也就不需要 比对 supposedRemoteAddr 和 raddr了
mutex.Lock()
dialedUDPConnMap[raddr.String()] = thisconn
mutex.RUnlock()
err = extractor.WriteUDPResponse(raddr, bs[:n])
if err != nil {
break
}
}
}(newConn, addr)
}
}
}