mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-08 14:13:22 +00:00
https://github.com/XTLS/Xray-core/pull/5526#issue-3804306341 --------- Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
135 lines
3.1 KiB
Go
135 lines
3.1 KiB
Go
package tun
|
|
|
|
import (
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/xtls/xray-core/common/buf"
|
|
"github.com/xtls/xray-core/common/net"
|
|
)
|
|
|
|
// sub-handler specifically for udp connections under main handler
|
|
type udpConnectionHandler struct {
|
|
sync.Mutex
|
|
|
|
udpConns map[net.Destination]*udpConn
|
|
|
|
handleConnection func(conn net.Conn, dest net.Destination)
|
|
writePacket func(data []byte, src net.Destination, dst net.Destination) error
|
|
}
|
|
|
|
func newUdpConnectionHandler(handleConnection func(conn net.Conn, dest net.Destination), writePacket func(data []byte, src net.Destination, dst net.Destination) error) *udpConnectionHandler {
|
|
handler := &udpConnectionHandler{
|
|
udpConns: make(map[net.Destination]*udpConn),
|
|
handleConnection: handleConnection,
|
|
writePacket: writePacket,
|
|
}
|
|
|
|
return handler
|
|
}
|
|
|
|
// HandlePacket handles UDP packets coming from tun, to forward to the dispatcher
|
|
// this custom handler support FullCone NAT of returning packets, binding connection only by the source addr:port
|
|
func (u *udpConnectionHandler) HandlePacket(src net.Destination, dst net.Destination, data []byte) bool {
|
|
u.Lock()
|
|
conn, found := u.udpConns[src]
|
|
if !found {
|
|
egress := make(chan []byte, 16)
|
|
conn = &udpConn{handler: u, egress: egress, src: src, dst: dst}
|
|
u.udpConns[src] = conn
|
|
|
|
go u.handleConnection(conn, dst)
|
|
}
|
|
u.Unlock()
|
|
|
|
// send packet data to the egress channel, if it has buffer, or discard
|
|
select {
|
|
case conn.egress <- data:
|
|
default:
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (u *udpConnectionHandler) connectionFinished(src net.Destination) {
|
|
u.Lock()
|
|
conn, found := u.udpConns[src]
|
|
if found {
|
|
delete(u.udpConns, src)
|
|
close(conn.egress)
|
|
}
|
|
u.Unlock()
|
|
}
|
|
|
|
// udp connection abstraction
|
|
type udpConn struct {
|
|
net.Conn
|
|
buf.Writer
|
|
|
|
handler *udpConnectionHandler
|
|
|
|
egress chan []byte
|
|
src net.Destination
|
|
dst net.Destination
|
|
}
|
|
|
|
// Read packets from the connection
|
|
func (c *udpConn) Read(p []byte) (int, error) {
|
|
data, ok := <-c.egress
|
|
if !ok {
|
|
return 0, io.EOF
|
|
}
|
|
|
|
n := copy(p, data)
|
|
return n, nil
|
|
}
|
|
|
|
// Write returning packets back
|
|
func (c *udpConn) Write(p []byte) (int, error) {
|
|
// sending packets back mean sending payload with source/destination reversed
|
|
err := c.handler.writePacket(p, c.dst, c.src)
|
|
if err != nil {
|
|
return 0, nil
|
|
}
|
|
|
|
return len(p), nil
|
|
}
|
|
|
|
func (c *udpConn) Close() error {
|
|
c.handler.connectionFinished(c.src)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *udpConn) LocalAddr() net.Addr {
|
|
return &net.UDPAddr{IP: c.dst.Address.IP(), Port: int(c.dst.Port.Value())}
|
|
}
|
|
|
|
func (c *udpConn) RemoteAddr() net.Addr {
|
|
return &net.UDPAddr{IP: c.src.Address.IP(), Port: int(c.src.Port.Value())}
|
|
}
|
|
|
|
// Write returning packets back
|
|
func (c *udpConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|
for _, b := range mb {
|
|
dst := c.dst
|
|
if b.UDP != nil {
|
|
dst = *b.UDP
|
|
}
|
|
|
|
// validate address family matches between buffer packet and the connection
|
|
if dst.Address.Family() != c.dst.Address.Family() {
|
|
continue
|
|
}
|
|
|
|
// sending packets back mean sending payload with source/destination reversed
|
|
err := c.handler.writePacket(b.Bytes(), dst, c.src)
|
|
if err != nil {
|
|
// udp doesn't guarantee delivery, so in any failure we just continue to the next packet
|
|
continue
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|