github.com/igoogolx/clash@v1.19.8/listener/tproxy/udp.go (about) 1 package tproxy 2 3 import ( 4 "net" 5 "net/netip" 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 UDPListener struct { 14 packetConn net.PacketConn 15 addr string 16 closed bool 17 } 18 19 // RawAddress implements C.Listener 20 func (l *UDPListener) RawAddress() string { 21 return l.addr 22 } 23 24 // Address implements C.Listener 25 func (l *UDPListener) Address() string { 26 return l.packetConn.LocalAddr().String() 27 } 28 29 // Close implements C.Listener 30 func (l *UDPListener) Close() error { 31 l.closed = true 32 return l.packetConn.Close() 33 } 34 35 func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (C.Listener, error) { 36 l, err := net.ListenPacket("udp", addr) 37 if err != nil { 38 return nil, err 39 } 40 41 rl := &UDPListener{ 42 packetConn: l, 43 addr: addr, 44 } 45 46 c := l.(*net.UDPConn) 47 48 rc, err := c.SyscallConn() 49 if err != nil { 50 return nil, err 51 } 52 53 err = setsockopt(rc, addr) 54 if err != nil { 55 return nil, err 56 } 57 58 go func() { 59 oob := make([]byte, 1024) 60 for { 61 buf := pool.Get(pool.UDPBufferSize) 62 n, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob) 63 if err != nil { 64 pool.Put(buf) 65 if rl.closed { 66 break 67 } 68 continue 69 } 70 71 rAddr, err := getOrigDst(oob[:oobn]) 72 if err != nil { 73 continue 74 } 75 76 if rAddr.Addr().Is4() { 77 // try to unmap 4in6 address 78 lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port()) 79 } 80 handlePacketConn(in, buf[:n], lAddr, rAddr) 81 } 82 }() 83 84 return rl, nil 85 } 86 87 func handlePacketConn(in chan<- *inbound.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { 88 target := socks5.AddrFromStdAddrPort(rAddr) 89 pkt := &packet{ 90 lAddr: lAddr, 91 buf: buf, 92 } 93 select { 94 case in <- inbound.NewPacket(target, target.UDPAddr(), pkt, C.TPROXY): 95 default: 96 } 97 }