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 }