github.com/igoogolx/clash@v1.19.8/listener/tunnel/udp.go (about)

     1  package tunnel
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  
     7  	"github.com/igoogolx/clash/adapter/inbound"
     8  	"github.com/igoogolx/clash/common/pool"
     9  	C "github.com/igoogolx/clash/constant"
    10  	"github.com/igoogolx/clash/transport/socks5"
    11  )
    12  
    13  type PacketConn struct {
    14  	conn   net.PacketConn
    15  	addr   string
    16  	target socks5.Addr
    17  	proxy  string
    18  	closed bool
    19  }
    20  
    21  // RawAddress implements C.Listener
    22  func (l *PacketConn) RawAddress() string {
    23  	return l.addr
    24  }
    25  
    26  // Address implements C.Listener
    27  func (l *PacketConn) Address() string {
    28  	return l.conn.LocalAddr().String()
    29  }
    30  
    31  // Close implements C.Listener
    32  func (l *PacketConn) Close() error {
    33  	l.closed = true
    34  	return l.conn.Close()
    35  }
    36  
    37  func NewUDP(addr, target, proxy string, in chan<- *inbound.PacketAdapter) (*PacketConn, error) {
    38  	l, err := net.ListenPacket("udp", addr)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	targetAddr := socks5.ParseAddr(target)
    44  	if targetAddr == nil {
    45  		return nil, fmt.Errorf("invalid target address %s", target)
    46  	}
    47  
    48  	sl := &PacketConn{
    49  		conn:   l,
    50  		target: targetAddr,
    51  		proxy:  proxy,
    52  		addr:   addr,
    53  	}
    54  	go func() {
    55  		for {
    56  			buf := pool.Get(pool.UDPBufferSize)
    57  			n, remoteAddr, err := l.ReadFrom(buf)
    58  			if err != nil {
    59  				pool.Put(buf)
    60  				if sl.closed {
    61  					break
    62  				}
    63  				continue
    64  			}
    65  			sl.handleUDP(l, in, buf[:n], remoteAddr)
    66  		}
    67  	}()
    68  
    69  	return sl, nil
    70  }
    71  
    72  func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) {
    73  	packet := &packet{
    74  		pc:      pc,
    75  		rAddr:   addr,
    76  		payload: buf,
    77  	}
    78  
    79  	ctx := inbound.NewPacket(l.target, pc.LocalAddr(), packet, C.TUNNEL)
    80  	ctx.Metadata().SpecialProxy = l.proxy
    81  	select {
    82  	case in <- ctx:
    83  	default:
    84  	}
    85  }