github.com/yaling888/clash@v1.53.0/listener/tunnel/udp.go (about) 1 package tunnel 2 3 import ( 4 "fmt" 5 "net" 6 7 "github.com/yaling888/clash/adapter/inbound" 8 "github.com/yaling888/clash/common/pool" 9 C "github.com/yaling888/clash/constant" 10 "github.com/yaling888/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 bufP := pool.GetNetBuf() 57 n, remoteAddr, err := l.ReadFrom(*bufP) 58 if err != nil { 59 pool.PutNetBuf(bufP) 60 if sl.closed { 61 break 62 } 63 continue 64 } 65 *bufP = (*bufP)[:n] 66 sl.handleUDP(l, in, bufP, remoteAddr) 67 } 68 }() 69 70 return sl, nil 71 } 72 73 func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, bufP *[]byte, addr net.Addr) { 74 pkt := &packet{ 75 pc: pc, 76 rAddr: addr, 77 payload: bufP, 78 } 79 80 ctx := inbound.NewPacket(l.target, pc.LocalAddr(), pkt, C.TUNNEL) 81 ctx.Metadata().SpecialProxy = l.proxy 82 select { 83 case in <- ctx: 84 default: 85 pkt.Drop() 86 } 87 }