Files
XTLS_Xray-core/proxy/tun/udp_fullcone.go
T

171 lines
3.9 KiB
Go

package tun
import (
"context"
"io"
"sync"
"time"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
)
// sub-handler specifically for udp connections under main handler
type udpConnectionHandler struct {
sync.RWMutex
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 *buf.Buffer) {
data.UDP = &dst
u.RLock()
conn, found := u.udpConns[src]
if found {
select {
case conn.egress <- data:
default:
errors.LogDebug(context.Background(), "drop udp with size ", data.Len(), " to ", dst.NetAddr(), " original ", conn.dst.NetAddr(), " > queue full")
data.Release()
}
u.RUnlock()
return
}
u.RUnlock()
u.Lock()
defer u.Unlock()
conn, found = u.udpConns[src]
if !found {
egress := make(chan *buf.Buffer, 1024)
conn = &udpConn{handler: u, egress: egress, src: src, dst: dst}
u.udpConns[src] = conn
go u.handleConnection(conn, dst)
}
// send packet data to the egress channel, if it has buffer, or discard
select {
case conn.egress <- data:
default:
errors.LogDebug(context.Background(), "drop udp with size ", data.Len(), " to ", dst.NetAddr(), " original ", conn.dst.NetAddr(), " > queue full 2")
data.Release()
}
}
func (u *udpConnectionHandler) connectionFinished(conn *udpConn) {
u.Lock()
// Close runs twice per flow; a newer conn may already own this src.
if u.udpConns[conn.src] == conn {
delete(u.udpConns, conn.src)
close(conn.egress)
}
u.Unlock()
}
// udp connection abstraction
type udpConn struct {
handler *udpConnectionHandler
egress chan *buf.Buffer
src net.Destination
dst net.Destination
}
func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) {
b, ok := <-c.egress
if !ok {
return nil, io.EOF
}
return buf.MultiBuffer{b}, nil
}
// Read packets from the connection
func (c *udpConn) Read(p []byte) (int, error) {
b, ok := <-c.egress
if !ok {
return 0, io.EOF
}
defer b.Release()
n := copy(p, b.Bytes())
if n != int(b.Len()) {
return 0, io.ErrShortBuffer
}
return n, nil
}
func (c *udpConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
for i, b := range mb {
dst := c.dst
if b.UDP != nil {
if b.UDP.Address.Family().IsDomain() {
errors.LogError(context.Background(), "impossible domain packet ", b.UDP, " reply via original target ", dst)
} else {
dst = *b.UDP
}
}
err := c.handler.writePacket(b.Bytes(), dst, c.src)
if err != nil {
buf.ReleaseMulti(mb[i:])
return err
}
b.Release()
}
return 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)
return nil
}
func (c *udpConn) LocalAddr() net.Addr {
return c.dst.RawNetAddr()
}
func (c *udpConn) RemoteAddr() net.Addr {
return c.src.RawNetAddr()
}
func (c *udpConn) SetDeadline(t time.Time) error {
return nil
}
func (c *udpConn) SetReadDeadline(t time.Time) error {
return nil
}
func (c *udpConn) SetWriteDeadline(t time.Time) error {
return nil
}