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" ) type packet struct { data []byte dest *net.Destination } // 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 []byte) { u.RLock() conn, found := u.udpConns[src] if found { select { case conn.egress <- &packet{ data: data, dest: &dst, }: default: errors.LogDebug(context.Background(), "drop udp with size ", len(data), " to ", dst.NetAddr(), " original ", conn.dst.NetAddr(), " > queue full") } u.RUnlock() return } u.RUnlock() u.Lock() defer u.Unlock() conn, found = u.udpConns[src] if !found { egress := make(chan *packet, 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 <- &packet{ data: data, dest: &dst, }: default: errors.LogDebug(context.Background(), "drop udp with size ", len(data), " to ", dst.NetAddr(), " original ", conn.dst.NetAddr(), " > queue full") } } 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 { handler *udpConnectionHandler egress chan *packet src net.Destination dst net.Destination } func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) { for { e, ok := <-c.egress if !ok { return nil, io.EOF } b := buf.New() _, err := b.Write(e.data) if err != nil { errors.LogDebugInner(context.Background(), err, "drop udp with size ", len(e.data), " to ", e.dest.NetAddr(), " original ", c.dst.NetAddr()) b.Release() continue } b.UDP = e.dest return buf.MultiBuffer{b}, nil } } // Read packets from the connection func (c *udpConn) Read(p []byte) (int, error) { e, ok := <-c.egress if !ok { return 0, io.EOF } n := copy(p, e.data) if n != len(e.data) { 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 { 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.src) 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 }