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  }